Introduction

In this post, I aim to demonstrate the use of Emacs org-mode for taking notes that incorporate programming code. The goal was to not only display code with proper formatting and syntax highlighting but also to execute it and show the results of specific code blocks, allowing readers to reproduce the entire analysis if desired. The challenge was to sift through available examples and documentation to put together a summary that brings something new to what’s already been written about this topic. Initially, I intended to focus solely on customizing the PDF output, but as I explored the documentation, I discovered other aspects that I believe are worth sharing, most notably, customization of HTML output. As a result, however, the post ended up being quite long thus, here is a brief overview of each main section:

  • Examples of how to work with code: Provides a simple description of the structure of a code block and how its main parts can influence the processing of code blocks.
  • Setup and configuration: Provide instructions for installed all the required packages and third-party software to reproduce the examples.
  • Processing code blocks: Provides a more detailed description of how code blocks are processed.
  • Customization: Provides examples of how to customize the processing and export of org files mainly for HTML and PDF export

The notes in sections 1 and 3 were taken from the official Org Manual and the community-driven documentation, Worg. For the Org Manual, the relevant section can be accessed online and within Emacs (C-h i, org-mode > Working with Source Code). For the Worg documentation, the relevant section is primarily available online, but the complete files can also be cloned for offline access.

Examples of how to work with code

Code in an org file can be written either as in-line or in blocks. Among the various block types (e.g., example, comment, quote), the one that allows formatting and evaluation is known as code block or live code block. The term ‘live code blocks’ reflects their ability to modify the document content dynamically.

Blocks are defined using the keywords #+begin_src and #+end_src. Additional keywords and arguments can be used to control formatting, evaluation, and export. These include:

  1. A name keyword
  2. A language argument
  3. One or more switches arguments
  4. One or more header arguments

Snippet 1 demonstrates the basic structure of code blocks (both in-line and block formats) and the position of all the above elements. The next sections describe these in more detail.

Structure of code blocks

1# Block
2#+name: <name>
3#+begin_src <language> <switches> <header_arguments>
4<body>
5#+end_src
6
7# In-line
8Some regular text, src_<language>[<header_arguments>]{<body>}
Code Snippet 1: Structure of code blocks.

Name keyword

The name keyword allows to insert a reference to a block within the org file. For code blocks, this means one can evaluate a block like a function and insert its results directly into the text (which is the main purpose of inline code blocks). In the example below, a dataset gets processed in an R code block, but the results (a table) appear later in the document after the dataset and selection process are described.

Snippet 2 shows part of the code block definition used to create the example above. Notice the name keyword in the first block (#+name: AirQuality on line 1), which gets referenced later to insert the results (#+RESULTS: AirQuality on line 11) near the end of the text.

Code fragments for the use of the name keyword to insert the results of a code block

 1#+name: AirQuality
 2#+begin_src R :session :exports both :colnames yes
 3## install.packages("dplyr")
 4library(dplyr)
 5## Rest of the code ...
 6#+end_src
 7
 8<Description of the dataset and selection process>
 9
10#+caption: airquality dataset
11#+RESULTS: AirQuality
Code Snippet 2: Code fragments for the use of the name keyword to insert the results of a code block. The results of the code block with the name code:name-example are inserted later in the file using the #+RESULTS: keyword.

Language argument

The language argument tells Emacs how to format, edit, and evaluate the code—provided that an interpreter for the language is available on the system, it is supported by Org, and it is properly configured in Emacs. Fortunately, Org provides out-of-the-box support for dozens of languages (a list that can be further extended through third-party packages). This is a powerful feature that allows combining the results of code blocks—written in different languages—all within a single document.

In the following example, Python is used to scrape a web page and transform a table (describing the list of languages with built-in support in Org-mode) from HTML into a pandas DataFrame. This result is then passed to an R block, where the DataFrame is filtered based on a list of computer languages of interest. Finally, the resulting table is inserted later in the document using the #+RESULTS: keyword.

Since this process requires several external packages, the example include an introductory section listing the commands for creating a virtual environment and installing all the required packages and language dependencies.

Table 1 shows a copy of the final output and Snippet 3 shows parts of the code blocks that produce it.

Table 1: Selected languages with built-in support in Emacs
Lang_NLanguageIdentifierDocumentationMaintainer
3C++cppob-doc-cThierry Banel
5CLIshellob-doc-shellMatthew Trzcinski
22LaTeXlatexob-doc-LaTeXnan
36Pythonpythonob-doc-pythonJack Kamm
37RRob-doc-RJeremie Juste

The names listed in the Identifier column are the ones used as the language argument (e.g. #+begin_src python, on line 3). Notably, in the second code block (line 10), the results of the first block are assigned to the variable df using the header argument :var df=code:ScrapTable(). Note the use of parentheses around the code block name; this indicates that it should be processed as a function call.

Code fragments for the use of multiple languages to process an input

 1#+name: code:ScrapTable
 2#+label: code:ScrapTable
 3#+begin_src python :session :results table :colnames yes
 4  import pandas as pd
 5  # Rest of the code
 6#+end_src
 7
 8#+name: Process-table
 9#+label: Process-table
10#+begin_src R session :var df=code:ScrapTable() :exports both :colnames yes :cache yes
11  library(dplyr)
12  ## Rest of the code
13#+end_src
14
15#+caption: *Built-In language support for selected languages on Emacs.*
16#+RESULTS: Process-table
Code Snippet 3: Example of the use of multiple languages to process an input.

Switch argument

The switch argument gives you more control over how code blocks are executed and exported. In the example below, the switches -n and -r were used to add line numbers to the code and remove any labels, respectively.

Snippet 4 shows the code block used to produce the example above. On line 5, a label (ref:upd-upg) is included. Due to the -r switch, this label is not exported to the HTML file but can still be used to reference a specific line in the text. Interestingly, if the output format is HTML and the variable org-html-head-include-scripts is set to true, hovering over the link will highlight the cited line.

Code block with switches

1#+begin_src bash -n -r
2  sudo apt update
3  sudo apt upgrade
4
5  sudo apt update && sudo apt upgrade -y      (upd-upg)
6#+end_src
Code Snippet 4: Example of the use of switches. The -n and -r switches will add line numbers and remove labels in the code, in this case it will remove the label (ref:upd-upg).

Other switches include, -k which will keep the labels, +n which will continue the line numbers from previous blocks or, if a number is passed along (e.g. +n 5) it will set the line number at that value.

Header arguments

Header arguments are probably the most important setting for controlling how code blocks behave, including how they’re evaluated or exported. In the examples above, a number of header arguments modified the code blocks locally. These arguments use a colon followed by the lowercase argument name (e.g. :session), though they can be specified in other ways (see the Header arguments section). Table 2 describes the ones we used in the examples.

Table 2: List of header arguments used in the examples above
ArgumentUsed inDescription
:sessionSnippets 2 and 3Allows two or more code blocks wot share the same environment including packages (and their functions) and variables.
:exports bothSnippets 2 and 3Exports the code and the results of the evaluation
:colnames yesSnippets 2 and 3Treats the first row of a table as a heading, also preservs the first row in the output.
:results tableSnippet 3Try to interpret the output as an org-table.
:var df=code:ScrapTable()Snippet 3Assigns the returned value of the block code:ScrapTable to the variable df. The parenthesis at the end of the block name are needed to interpret the block as a function and not verbatim.
:cache yesSnippet 3Caches the results of the code block evaluation in cases where the inputs have not changed.

Default values for these and other arguments are specified in the variables org-babel-default-header-args and org-babel-default-inline-header-args which control how code blocks and in-line code are interpreted, respectively. Table 3 describes the default values.

Table 3: Default values for code blocks and in-line code
ArgumentBlockIn-lineDescription
:sessionnonenoneEvery code block is evaluated in a new environment
:resultsreplacereplaceEvery time a block is evaluated the newest results replace the previous one
:exportscoderesultsThe former exports only code, the latter only the results of the evaluation.
:cacheno-No caching is attempted.Blocks are evaluated each time.
:nowebno-References to other blocks <<ref>> are not expanded.
:hlinesnoyesPreserves horizontal lines in a table.
:tangleno-Code is not exported to an external file.

The arguments and values described above are just a subset of what’s available for configuring how code blocks are processed. The number of available arguments and values is large enough that they’re organized into different categories (see the Processing code blocks section).

Setup and configuration

To reproduce the examples presented in the previous section, it is necessary to install some Emacs packages and third-party software, most notably.

  • Emacs 30+
    • Org
  • Git
  • Python with the following packages
    • numpy
    • pandas
    • bs4
    • lxml
    • requests
    • pygments
  • R with the following packages
    • dplyr
  • A TeX distribution with some specific engines, scripts and packages
    • Distributions:
      • TeX Live: Available for Linux, Windows and MacOS
      • MiKTeX: Also available for Linux, Windows and MacOS
      • MacTeX: TeX Live with specific tools for Mac Os
    • Required engines:
      • XeLaTeX
    • Required scripts:
      • latexmk
    • Required packages:
  • inputenc
  • fontenc
  • geometry
  • fontspec
  • ulem
  • xcolor
  • amsmath
  • amssymb
  • booktabs
  • longtable
  • colortbl
  • rotating
  • sidecap
  • graphicx
  • wrapfig
  • hyperref
  • placeins
  • caption
  • minted
  • fancyvrb
  • newfloat

Instructions on how to install all the requirements is provided below.

Emacs

Packages

Sample configuration files are available on a cloud storage service ( ). A description of the configuration is provided in the sections Org configuration and Emacs customization. Additionally, I maintain a GitHub repository with my Emacs configuration ( ) , which includes further customizations.

Org configuration

Babel

At the core of Org’s ability to process code is Babel. Originally an extension, it is now part of core Org and defines which languages can be executed. By default, only emacs-lisp is enabled for evaluation, but additional languages can be enabled by extending the variable org-babel-load-languages. Snippet 5 shows an example of how to include R and Python in this list. This code should be placed in your Emacs configuration file.

Code to extend the list of languages that can be evaluated in an org-file

(org-babel-do-load-languages
     'org-babel-load-languages
     '((python . t)
       (R . t)
       ))
Code Snippet 5: Emacs lisp code to extend the list of languages that can be evaluated in an org-file.

See the community documentation for a list of supported languages and the corresponding identifier (which should be used in the code above).

Header arguments

Header arguments can be configured at different levels: globally, per file, per section, and per block (locally). Local settings take precedence over higher-level ones. Snippet 6 shows an example from the official documentation to set the :noweb header argument to yes globally, affecting both blocks and in-line code.

Code to customize header arguments

(setq org-babel-default-header-args
           (cons '(:noweb . "yes")
                 (assq-delete-all :noweb org-babel-default-header-args)))
Code Snippet 6: Emacs lisp code to customize header arguments.

At the file level, customization can be specified using the #+PROPERTY: keyword at the top of the file. For example, #+PROPERTY: header-args :exports both sets all code blocks to export both the code and the evaluation results by default. Language-specific configurations can also be set, such as #+PROPERTY: header-args:R :session *R*, which sets the default session for R code to *R*.

At the section level, customization is specified inside a PROPERTIES drawer and applies to all child subsections (Snippet 7). Language-specific settings are applied using :HEADER-ARGS:<lang>.

Code to customize header arguments at the section level

* Sample section
:PROPERTIES:
:HEADER-ARGS: :cache yes
:END:
Code Snippet 7: Emacs lisp code to customize header arguments at the section level. These customization will be inherited by child subsections.

The examples in the previous section showed header arguments specified after the language (or switches, if any). However, they can also be included using the #+HEADER: keyword.

As for in-line code, header arguments are specified within square brackets for example, src_R[:exports both]{nrow(mytable)}.

Third-party software

The third-party software required can be installed inside an Anaconda virtual environment for easier management and removal. Snippets 8, 9 and 10 list all the commands needed to set up a virtual environment and install the required software.

Code to install miniconda, create a new virtual environment and install third-party software

# Install linux dependecies
# sudo apt install libharfbuzz-dev libfribidi-dev libfreetype6-dev libpng-dev libtiff5-dev libjpeg-dev libcurl4-openssl-dev libxml2-dev

# Miniconda installation
# miniconda_dir="~/.local/bin/miniconda3"
# miniconda_url="https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh"

# wget --tries=2 --retry-connrefused --waitretry=10 "$miniconda_url"
# chmod +x Miniconda3-latest-Linux-x86_64.sh
# bash Miniconda3-latest-Linux-x86_64.sh -b -p "${miniconda_dir}"

# "${miniconda_dir}"/condabin/conda init "$(echo $SHELL | awk -F/ '{print $NF}')"
# eval "$($miniconda_dir/bin/conda shell.bash hook)"

# Add additional channels
conda config --add channels conda-forge
conda config --add channels bioconda

# Create a virtual environment
conda create --name webscrap -y
conda activate webscrap

# Install python libraries
conda install anaconda::pandas
conda install anaconda::bs4
conda install conda-forge::lxml
conda install conda-forge::pygments
conda install anaconda::requests

# Install R
conda install r::r
Code Snippet 8: Code to install a smaller version of Anaconda (miniconda), create a new virtual environment and install third-party software.

Then, on an R session install the required R packages.

Installation of R packages for processing dataframes

install.packages("dplyr")
Code Snippet 9: Installation of R packages for processing dataframes.

For exporting to PDF, the following LaTeX packages should be installed:

Installation of LaTeX requirements for producing the PDF examples

# For TexLive and MacTeX
sudo tlmgr install xetex latexmk inputenc fontenc geometry\
     fontspec ulem xcolor amsmath amssymb booktabs longtable\
     colortbl rotating sidecap graphicx wrapfig hyperref\
     placeins caption minted fancyvrb newfloat

# For MiKTeX
# mpm --install=xetex latexmk inputenc ...
Code Snippet 10: Installation of LaTeX requirements for producing the PDF examples.

Processing code blocks

Having all the required software properly configured, we can discuss with more detail how code blocks are processed by Org. The official documentation identifies three operations: evaluation, export, and tangling. Evaluation determines whether code gets executed and how the results are handled. Export controls whether code and results appear in different output formats (like HTML or PDF) and how they’re presented. Tangling extracts code from blocks to create standalone source files that can run as executable programs. Together, these three operations make Emacs a powerful literate programming environment

Evaluate

Because of the risk that carry the execution of code, by default, babel allows the execution of only Emacs-Lisp code and prompts for the user’s permission before its execution. As it will be described in this section (and the Babel section above) this behavior can be modified to streamline the processing however, care must be taken specially when working with files written by someone else.

There are several ways to evaluate code blocks in Org mode. The examples above demonstrate evaluation during export (i.e., when generating an HTML or PDF file). Alternatively, code can be evaluated interactively using the keybindings C-c C-v e or C-c C-c, which inserts the results below the code block under a #+RESULTS keyword. Also, code blocks can be evaluated and explicitly place their results elsewhere in the document using the #+RESULTS: or #+CALL: keywords. The latter is particularly useful, as it allows you to pass header arguments and specify variables for example, #+CALL: my-Rblock[:session *R*](n=5). For in-line code the equivalent will be call_my-Rblock[:session *R*](n=5).

Code block evaluation can be further customized using the header argument, :eval which accepts the values listed in Table 4

Table 4: Possible values for the :eval argument.
ValueDescription
yesAllows evaluation
noNever evaluates
queryPrompts for permission during export and interactively.
never-exportDo not evaluate during export but it can be done interactively.
query-exportPrompts for permission during export.

If the header argument is not defined, Org will default to the value set in the variable org-confirm-babel-evaluate.

For longer documents with multiple code blocks, evaluation can be time-consuming. To optimize performance, you can cache results using the :cache yes header argument. This is particularly useful when certain blocks consistently return the same output for the same input. Behind the scenes, Org determines whether a block needs re-evaluation by checking a generated SHA1 hash.

Handling the results of the evaluation

The handling of results is controlled by several header arguments, the most important of which is :results. This argument accepts multiple values, categorized into four distinct types:

  1. Collection: How are the results collected
  2. Type: What kind of results are expected.
  3. Format: How Org process the results.
  4. Handling: How the results are inserted after processing.

Below is a description of each type:

Collection

Controlled by the values value or output. The former is the default for most languages and interprets the code as a function that returns the last evaluated expression. The later (also known as scripting mode) sends the code to an interpreter and returns the same values that would be printed to standard output.

Type

Code blocks can return various types of output, including:

  • table / vector: Attempts to interpret the result as an org-table. Horizontal lines are not included unless the argument :hlines yes is included. By default, tabs are used as field separator but this can be customized with the :sep argument.
  • list: Attempts to interpret the result as an org-list.
  • scalar / verbatim: Returns the results as raw text wrapped in an example block.
  • file: Saves the result to a filename and inserts a link after the code block. The name of the file is specified with the :file <name> argument. The description used for the link can be customized with the :file-desc <description> argument. The directory where to save the file can be specified with the :output-dir <path> argument. File permissions are set with the :file-mode (identity #oXYZ) where XYZ will define the file permission for the owner, group and others as it is normally do in Unix systems.
Format

The results of a code block evaluation are inserted below a #+RESULTS keyword. Additionally, these results can be wrapped in a block of a particular format, controlled by one of the following values:

  • raw: The output is interpreted as raw text.
  • code: The output is wrapped in a code block with the language being the same as the code block evaluated.
  • drawer: The output is interpreted as raw text but wrapped inside a :results: drawer.
  • html: The output is wrapped in an export block of the html type, #+begin_export html
  • latex: The output is wrapped in an export block of the latex type, #+begin_export latex
  • org: The output is wrapped in a code block of the language org
  • pp: In the case of Emacs-Lisp, Python or Ruby, the output is converted to ‘pretty-print’ code.

Related to the above, the header argument, :wrap <string1> <string2> will wrap the results in a block, #+begin_<string1> <string2>

Handling

These values control whether and how the results are inserted into the buffer.

  • replace: The default value, it causes the results to replace any previous output.
  • silent: The results are not inserted but echoed in the minibuffer.
  • none: The results are computed but not inserted nor echoed. These can still be referenced.
  • discard: No results are inserted or exported. This value can be useful for the side-effects a code can have such as downloading files or setting environmental variables.
  • append and prepend. Adds the latest results to the bottom and the top of the #+RESULTS keyword, respectively.

Export

Exporting an Org file is useful for sharing content with individuals who do not use Emacs, for example, by printing it or publishing it on the web. For code blocks, Org provides a header argument to control whether the code, its results, both, or neither are included in the exported output. This argument is :exports, and its possible values are:

  • code: The default value, it exports only the code.
  • results: Exports the results of the evaluation but not the code.
  • both: Code and the results are included in the exported file.
  • none: Nothing is exported. The evaluation of the code would depend on other arguments such as :eval (see )

Tangle

While exporting produces a file where code and documentation are combined (woven) together, it is also possible to generate one or more files containing only code, which can then be used as executable files. This process is known as tangling. The following header arguments control various aspects of this process:

  • :tangle: Default value no. If a yes value is passed, the code will be written to a file whose name is derived from the name of the org file. If another string is passed that would be used as the file name for the source code. In this case an extension should be provided, for example :tangle update_cmds.sh.
  • :mkpdir: Default value no. If a yes value is passed, it allows the creation of directories, provided that these are included in the :tangle argument.
  • :comments: Allow the inclusion of comments outside those in the code. Possible values include:
    • no: Default, no extra comments are included
    • link: Include org-links that point to the org heading where the code is located. In the tangled file one can jump back to the source org file using the function org-babel-tangle-jump-to-org
    • org: Include the name of the org heading and all the text between it and the code block. However, no link to the source file are provided.
    • both: Combines the information of org and link combined in that order. That is the link to the org file is above the first line of the tangled code. If more than one code block tangles to the same output file and the code blocks are tangled using the :comments link or both then extra comment lines will be included to mark the end of the code from each block
    • noweb: If noweb is enabled (e.g. :noweb yes), then it will expand the references, adding a link to their definition.
  • :shebang: Used to insert the path to the executable for the tangled file, for example, :shebang "#!/bin/bash"; it will also change the tangled file’s permission to allow execution.
  • :tangle-mode: It can further customize the file’s permission. For example, to set the tangled file to be read and write only by the owner one could write, :tangle-mode (identity #o600) or simply :tangle-mode o600. Note that if the tangled file has been previously created with different permissions this argument will not change them.

Customization

Having described the processing of code blocks, we now explore how to customize key aspects of this process, specifically evaluation and export.

Code evaluation

For trusted files, confirmation for the evaluation of code blocks can be skipped by setting the variable org-confirm-babel-evaluate to nil inside a local variable box. Snippet 11 shows how can this be done inside a heading that would not be exported.

Setting a local variable to automate code evaluation

* Local variables                              :noexport:
# Local Variables:
# org-confirm-babel-evaluate: nil
# End:
Code Snippet 11: Setting a local variable to automate code evaluation.

Local variables can be used to customize other aspects of a file such as the export process as it is described below.

HTML export

Exporting an org-file to HTML is a straightforward process. Emacs has a default exporter that you can access using the keybinding C-c C-e followed by h h. This sequence calls the function org-export-dispatch, which opens a menu with various export options typically selected by typing two letters in sequence. Here, h h selects Export to HTML followed by As HTML file. The examples shown in the section Examples of how to work with code were produced this way. Note that titles and headings have larger font sizes and weights, and code has a different font, a border, and syntax highlighting. Importantly, all these customizations are embedded in the HTML file (no extra CSS or JS files).

While the default output is generally sufficient, there are areas that could be improved, such as table formatting or code highlighting. Customization options include using packages, theme files, and even creating custom backends for export. A few of these options are described below but before that a brief description of how Org highlights code is provided.

Org’s default highlighting system

Org includes a built-in system for highlighting code snippets when exporting to HTML, controlled by the variable org-html-htmlize-output-type. Table 5 lists the three possible values for this variable. By default, Org generates CSS styling based on the current theme’s faces and includes these styles inline in the exported HTML file. If the value is set to css, Org will include only the CSS selectors in the output; the corresponding style definitions can then be generated using the function org-html-htmlize-generate-css. Finally, if the value is set to plain, the code will be exported as plain text without any styling.

Table 5: Possible values for the org-html-htmlize-output-type variable.
ValueDescription
inline-cssDefault. Includes CSS attributes in the HTML file.
cssIncludes CSS selectors only.
plainNo CSS definitions or selectors are included.

The appearance of code blocks within an Org buffer is governed by the variable org-src-fontify-natively. This is enabled by default (t), meaning Org applies syntax highlighting to source blocks. Syntax highlighting depends on the configuration of the corresponding major mode, which defines language-specific rules for highlighting, indentation, and editing. When a major mode is available for the language used in a source block, Org activates it in the background and applies its font-lock rules for styling.

In addition to the CSS used for syntax highlighting, Org provides a default CSS stylesheet that applies to other elements such as headlines, tables, and code blocks. This is controlled by the variable org-html-head-include-default-style. This is also enabled by default (t), meaning the default stylesheet will be included alongside any syntax highlighting styles.

Using a theme

As will be discussed in the section Using the custom css, orgcss, Org provides a way to include CSS and JS files in the exported HTML file. Many users have created custom files to personalize the output, and these have been collected in a GitLab repository and embedded in ‘theme’ files. These files allow you to load all configurations using the #+SETUPFILE keyword. Snippet 12 shows two examples of how themes are loaded, and below it, the HTML files produced are presented. The first theme (bigblow_inline) adds a navigation bar with all sections and clickable elements that can collapse and expand all subsections (if any). The second theme (stylish_white) is simpler, changing mainly the font face. In both cases, the tables are better formatted that the original, while the code has the same colors as the original but without the background.

Example of the use of theme files

#+SETUPFILE: ./org-themes/bigblow_inline/bigblow_inline.theme
#+SETUPFILE: ./org-themes/stylish_white/stylish_white.theme
Code Snippet 12: Example of the use of theme files.

Using the custom css, orgcss

Org provides two keywords for adding HTML code to the <head> element of the exported HTML file: #+HTML_HEAD: and #+HTML_HEAD_EXTRA:. Snippet 13 demonstrates how to include a local CSS file in the exported HTML using the #+HTML_HEAD: keyword. This keyword is intended for essential information, while #+HTML_HEAD_EXTRA: is meant for additional content, such as a JavaScript (JS) file.

Keywords for adding extra styles to the exported HTML

#+HTML_HEAD: <link rel="stylesheet" href="my_custom.css">
Code Snippet 13: Keywords for adding extra styles to the exported HTML.

With the above keywords, you can use any of the custom CSS files collected in the Org Themes collection website or found elsewhere. One that stands out is orgcss due to its simple design and detailed instructions for use (including integration with a dedicated syntax highlighter, described in the next section). Snippet 14 shows how the CSS style is specified using the #+HTML_HEAD: keyword (line 1).

Importantly, to produce the HTML file, two Org variables must be changed from their default values. First, the variable org-html-head-include-default-style should be set to nil (as in line 5) to prevent the inclusion of the CSS definitions in the variable org-html-style-default. Second, the variable org-html-htmlize-output-type should be set to either css or nil to prevent the definition of classes within the HTML file.

Local variables setup for using orgcss

1#+HTML_HEAD: <link rel="stylesheet" href="./css/org.css">
2
3* Local variables                              :noexport:
4# Local Variables:
5# eval: (setq org-html-head-include-default-style nil)
6# eval: (setq org-html-htmlize-output-type 'nil)
7# End:
Code Snippet 14: Local variables setup for using orgcss.

The results of using the above settings are shown below. Note that there aren’t many changes, headings have a different color, code blocks are framed and the language is indicated in a shaded box on the top right corner.

Using highlight.js

The CSS file provided by orgcss doesn’t include definitions for highlighting code blocks. However, it provides instructions for using the dedicated syntax highlighter highlight.js, which supports for over 190 languages. By ‘support’ it is meant that different code elements (i.e. tokens) are colorized according to their specific semantic meaning, rather than the more generic approach that Org’s default highlighting system uses.This shouldn’t be confused with the support for evaluating code discussed in the Language argument section.

Snippet 15 shows how to use highlight.js via CDN (e.g. cdnjs). Line 1 loads a CSS file for a specific color theme (see https://highlightjs.org/examples for more examples). Line 3 loads a minified version of the highlight.js library with support for about 40 languages (see the official list of supported languages). Since this doesn’t include R, line 5 will load an additional library for that language. Finally, line 7 defines a custom function that modifies the code blocks and applies the styling above. The result of exporting to HTML appears below the code block.

Loading orgcss along with highlight.js with custom languages

1#+HTML_HEAD: <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/googlecode.css">
2
3#+HTML_HEAD_EXTRA: <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
4
5#+HTML_HEAD_EXTRA: <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/languages/r.min.js"></script>
6
7#+HTML_HEAD: <script>var hlf=function(){Array.prototype.forEach.call(document.querySelectorAll("pre.src"),function(t){var e;e=t.getAttribute("class"),e=e.replace(/src-(\w+)/,"src-$1 $1"),console.log(e),t.setAttribute("class",e),hljs.highlightBlock(t)})};addEventListener("DOMContentLoaded",hlf);</script>
Code Snippet 15: Loading orgcss along with highlight.js with custom languages.

Using the package, nice-org-html

The nice-org-html package offers a straightforward way to customize HTML exports using any Emacs theme available on the system. A dark and a light themes should be specified for export, with optional additions of headers, footers, and custom CSS/JS files. The official repository provides installation and configuration instructions. Notably, the package adds an export option to the org-export-dispatch menu (accessed via keys H h) which will produce an HTML file. Snippet 16 shows a sample configuration using two non-default themes (See the online theme gallery for additional theme options). Below the code is the resulting HTML export.

Sample configuration for the nice-org-html package

(use-package nice-org-html
  :straight (nice-org-html :type git
                           :files (:defaults "code/*.el" "code/*.css" "code/*.js" "nice-org-html-pkg.el")
                           :host github
                           :repo "ewantown/nice-org-html")
  :config
  (setq nice-org-html-theme-alist
        '((light . kaolin-valley-light)
          (dark  . kaolin-valley-dark)))
  (setq nice-org-html-default-mode 'dark)
  (setq nice-org-html-headline-bullets nil)
  (setq nice-org-html-options '(:layout "compact" :collapsing t :src-lang t))
  :hook (org-mode . nice-org-html-mode)
  )
Code Snippet 16: Sample configuration for the nice-org-html package. The configuration uses the package straight.el and use-package.

T By default, the configuration sets the dark theme as the primary display, which can be toggled using the sun/moon icon in the upper right corner. The opposite corner features a menu icon that reveals the table of contents. Code blocks include syntax highlighting (based on Org’s default highlighting system) and each block has a copy button in the upper right corner. Tables span the width of the main text area and appear better formatted than the default output.

Use Pygments with a custom backend

Pygments is a Python-based syntax highlighter that supports more languages than highlight.js, including Emacs Lisp. It is easily applied when exporting to PDF (see next section). For HTML, however, it requires defining a custom backend exporter.

Snippet 17 is a modification of the code described in Anton Linevych’s blog post. It sets up three variables: one for the CSS filename, another for the style, and a third for the Pygments binary path (see Settings for pygments). A custom function then creates the CSS file and adds a linking line to the exported HTML. Next, another custom function processes code blocks using Pygments. An export function is then defined to specify which buffer elements will be processed. Finally, the custom backend combines these functions and adds a menu entry to org-export-dispatch.

Definition of a custom backend for highliging code with Pygments for HTML export

;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Settings for pygments ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CSS file name, style and path to executable
(defvar-local my/css-path "pygments.css")
(defvar-local my/pygments-style "default")
(defvar pygments-path "pygmentize")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Function to create a CSS file and point to it ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun my/ensure-pygments-css ()
    "Ensure pygments.css exists in the current directory and set org-html-head."
      (message "Creating pygments.css...")
      (shell-command (format "%s -S %s -f html -a .highlight > %s" pygments-path my/pygments-style my/css-path))
    ;; Always set org-html-head to use the CSS file
    (setq-local org-html-head (format "<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\" />" my/css-path))
    (message "Using pygments.css for syntax highlighting"))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Function to process codeblocks with Pygments ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun my/pygments-org-html-code (code contents info)
  "Process code blocks with Pygments."
  (message "DEBUG: my/pygments-org-html-code called!")
  ;; Generating tmp file path using current time hash
  (let* ((temp-source-file (format "/tmp/pygmentize-%s.txt" (md5 (current-time-string))))
         (lang (org-element-property :language code))
         (code-content (org-element-property :value code)))
    (message "DEBUG: Processing %s code block" lang)
    ;; Writing block contents to the file
    (with-temp-file temp-source-file
      (insert code-content))
    ;; Executing the shell-command and reading output
    (let ((highlighted-output (shell-command-to-string
                              (format "%s -l \"%s\" -f html  %s"
                                      pygments-path
                                      (or lang "")
                                      temp-source-file))))
      (message "DEBUG: Highlighted output length: %d" (length highlighted-output))
      highlighted-output)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Define a new export function ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun my/org-export-to-html-pygments (&optional async subtreep visible-only body-only ext-plist)
  (interactive)
  (my/ensure-pygments-css)
  (org-export-to-file 'pygments-html (org-export-output-file-name ".html" subtreep)
    async subtreep visible-only body-only ext-plist))


;;;;;;;;;;;;;;;;;;;;;;;;
;; Define new backend ;;
;;;;;;;;;;;;;;;;;;;;;;;;
(org-export-define-derived-backend 'pygments-html 'html
  :translate-alist '((src-block . my/pygments-org-html-code))
  :menu-entry '(?Y "Export to HTML with Pygments"
                   ((?h "As HTML file" my/org-export-to-html-pygments))))
Code Snippet 17: Definition of a custom backend for highliging code with Pygments for HTML export.

The variables for the CSS filename and style are local and can be customized per buffer within a local variables block (see Snippet 18).

Example of how to set the local varibles for the CSS file and the style used

# Local Variables:
# my/css-path: "css/pygments.css"
# my/pygments-style: "staroffice"
# End:
Code Snippet 18: Example of how to set the local varibles for the CSS file and the style used.

The result of these settings is shown below. Similar to orgcss , the variable org-html-htmlize-output-type should be set to either css or nil whereas, the variable org-html-head-include-default-style can remain at its default value (t), as in the example below. This will keep the background color and left margin of code blocks.

PDF export

Exporting to PDF uses the same base function as HTML (org-export-dispatch) with the only difference being the last two keys, l p. These will select the options, Export to LaTeX followed by Export as PDF file. As for the style, similar to the HTML export, the default PDF output is quite simple: the title and headings appear in a bold, larger font than the regular text, which itself differs from the font used for code (see below).

LaTeX customization

Because Org produces PDF files through LaTeX, most customizations are achieved using LaTeX packages. I previously wrote a post describing basic usage of Emacs’ org-mode for LaTeX documents, so this post focuses specifically on the packages used to produce the output shown below.

Before describing these packages, it’s important to highlight the main differences from the default output. First, the fonts for both regular text and code were modified. The code blocks are now surrounded by colored boxes and tagged with labels indicating whether they represent input or output. When code blocks occur at the end of a page, they split across pages rather than moving entirely to the next page. Additionally, syntax highlighting was added to the code.

For code captions, the label was changed from ‘Figure’ to ‘Snippet’ and numbered according to section. Finally, output tables were customized with thicker horizontal lines at the top and bottom, a thinner line separating the header from the body, and increased row height.

Snippet 19 shows all the customizations used to produce the output above, and table 6 provides a general description of what each package does. Package-specific commands (e.g. \geometry) and customization of defaults (e.g. \arraystretch) are described after the table.

LaTeX configuration to customize the PDF output

 :latex-options:
 #+LATEX_CLASS_OPTIONS: [12pt]
 #+LATEX_HEADER: \usepackage{geometry,fontspec}
 #+LATEX_HEADER: \usepackage[x11names]{xcolor}
 #+LATEX_HEADER: \usepackage{booktabs,colortbl,rotating,sidecap}
 #+LATEX_HEADER: \usepackage{wrapfig,placeins,caption,minted,fancyvrb,newfloat}

 #+LATEX_HEADER: \geometry{paper=a5paper,vmargin={2cm,2cm}, hmargin={1.5cm,1.5cm} }
 #+LATEX_HEADER: \pagestyle{empty}
 #+LATEX_HEADER: \renewcommand{\arraystretch}{1.5}
 #+LATEX_HEADER: \setlength{\parindent}{0pt}
 #+LATEX_HEADER: \renewcommand{\floatpagefraction}{.75}
 #+LATEX_HEADER: \captionsetup{labelfont=bf, font=footnotesize}
 #+LATEX_HEADER: \hypersetup{colorlinks=true, citecolor=DeepSkyBlue4, linkcolor=DeepSkyBlue4, urlcolor=DeepSkyBlue4}

 #+LATEX_HEADER: \setmainfont{RobotoCondensed}[UprightFont=*-Light, BoldFont=*-Regular, ItalicFont=*-LightItalic, BoldItalicFont=*-Italic]
 #+LATEX_HEADER: \setmonofont{IosevkaTerm NF}

#+LATEX_HEADER: \setminted{style=manni,fontsize=\scriptsize, frame=single, baselinestretch=1.2, label=Input, rulecolor=DarkOliveGreen4}
 #+LATEX_HEADER: \fvset{tabsize=4, obeytabs=true}

 #+LATEX_HEADER: \DefineVerbatimEnvironment{verbatim}{Verbatim}{frame=lines,fontsize=\scriptsize,breaklines=true,rulecolor=\color{DarkSeaGreen4},label=Output}
 #+LATEX_HEADER: \newenvironment{code}{\captionsetup{labelfont=bf, type=listing, name=Snippet, aboveskip=-3pt ,belowskip=15pt}}{}
 #+LATEX_HEADER: \SetupFloatingEnvironment{listing}{name=Snippet,within=section}
 :END:
Code Snippet 19: LaTeX configuration to customize the PDF output.
Table 6: List of packages used for the customization of PDF export.
PackageDescription
inputencSpecifies the input encoding, in this case UTF-8 (redundant in Lua/XeLaTeX; use only with PDFLaTeX).
fontencImproves font encoding for hyphenation and special characters (essential for PDFLaTeX).
geometryAllows to personalize the page margins and layout.
fontspecAllows to configure the system fonts (Lua/XeLaTeX only).
ulemUnderline/emphasis package. The option normalem prevents \emph to be interpreted as underline.
xcolorExtends color support. The option X11 provides the largest palette.
amsmathImproves the use of mathematical formulas.
amssymbExtends the use of math symbols.
booktabsProvides defaults for drawing tables such as top and mid rules even if these are not specified.
longtableMulti-page tables.
colortblTable cell coloring.
rotatingRotated floats (e.g., sidewaystable).
sidecapSide captions for figures/tables.
graphicxImage inclusion (\includegraphics).
wrapfigText-wrapped figures.
hyperrefPDF hyperlinks (better to load last; exceptions: cleveref, footnotehyper).
placeinsPrevents float leaks (\FloatBarrier).
captionCustom captions (e.g., \captionsetup).
mintedSyntax-highlighted code (requires Python).
fancyvrbImproved verbatim text.
newfloatCustom float environments (e.g., listing).

Additionally, the following commands are worth mentioning for modifying the output of tables and code blocks:

  • \arraystretch: Used to increase the height within tabular, array, and matrix environments. Its default value is 1.0
  • \floatpagefraction: Used to increase the percentage that a float (e.g. tables, figures) can occupy in a given page. Its default value is 0.5
  • \captionsetup: From the caption package, used to set the font of captions to be bold and the size of footnotes.
  • \setmainfont: From the fontspec package. Changes the default font (used for regular text) to RobotoCondensed. Specific font variations (e.g. bold) are derived from the root name (e.g. BoldFont=*-Regular).
  • \setmonofont: Also from the fontspec package. Changes the default font for the command \texttt, verbatim environments, or code blocks.
  • \DefineVerbatimEnvironment: From the fancyvrb packages defines a new verbatim environment for code output. It has a custom style for the font size, the presence of lines surrounding the code which are colored.
  • \fvset: Also from the fancyvrb package, defines global options for the Verbatim and SaveVerbatim environments, in this case, setting the size of tabs and forcing they to be displayed as tab characters.
  • \setminted: From the minted package. Configures the formatting of code blocks using pygments. The options modified include changing the default style, the font size, the lines used for the box surrounding the code, its color and a label that is meant to distinguish code input from output.
  • \SetupFloatingEnvironment: From the caption package, configures the numbering of the listing environment to be set within section.
  • \newenvironment: From the newfloat package, creates a new environment code, that behaves as a listing environment. Its caption is customized to have a given label in bold face and also a defined vertical space before and above the code block. This new environment was created for code blocks that have a caption and span two pages (see a related question on tex.stackexchange).

Emacs customization

The last command in the previous section, \newenvironment{code}{...}{}, requires all code blocks to be enclosed in a custom code environment. However, Org exports code blocks within a listing environment. To resolve this mismatch, a custom function was written to replace all listing environments with the new code environment. Snippet 20 shows its definition (lines 1 to 7) and its addition to the variable org-export-filter-final-output-functions (line 10). This ensures the function is applied to the final textual output of an Org export. An example of its use can be seen between pages 1 and 2 in the sample PDF.

Definition of a function to replace a listing environment with a code one

 1(defun my/org-latex-replace-listing-env (output backend info)
 2  "Replace LaTeX 'listing' environments with 'code' environments in final LaTeX output."
 3  (when (org-export-derived-backend-p backend 'latex)
 4    (replace-regexp-in-string
 5     "\\\\begin{listing}\\(\\[[^]]*\\]\\)?\\(\\(?:.\\|\n\\)*?\\)\\\\end{listing}"
 6     "\\\\begin{code}\\2\\\\end{code}"
 7     output)))
 8
 9
10  (add-hook 'org-export-filter-final-output-functions
11            #'my/org-latex-replace-listing-env)
Code Snippet 20: Definition of a function to replace a listing environment with a code one.

A more critical customization involves specifying the backend used to format code blocks (source code listings). This is controlled by the variable org-latex-src-block-backend which accepts four possible values: verbatim, listings, minted and engraved. The last two offer the most flexibility, particularly in font customization (including syntax highlighting). Snippet 21 shows how this can be set on a per-buffer basis using local variables. When opening a file with such settings, Emacs will prompt for confirmation. Alternatively, changes can be reapplied by running the command normal-mode and confirming the prompt.

Local variable specification to use minted as backend export

# Local Variables:
# eval: (setq org-latex-src-block-backend 'minted)
# End:
Code Snippet 21: Local variable specification to use minted as backend export.

Finally, the LaTeX compilation command was modified to pause if an error occurs. Snippet 22 compares the default command (commented out) with the customized version. All the customizations mentioned above are included in the provided configuration file.

Customization of the commands used to process LaTeX files to produce a PDF

;; ("latexmk -f -pdf -%latex -interaction=nonstopmode -output-directory=%o %f")
(setq org-latex-pdf-process (list "latexmk -xelatex %f")
Code Snippet 22: Customization of the commands used to process LaTeX files to produce a PDF.

Final thoughts

The sections above try to offer a good foundation for working with code in Org files. However, it should be noted that it still leaves a number topics unexplored. One that I plan to include in the future is an example of higher complexity that connects different blocks and showcases the use of different header arguments. Another topic would be trying to combine the different options to customize the HTML export such as the use of a dedicated syntax highlighter with the styling of the HTML document (using navigation bars) and the use of dark/light themes. These would be available in the same repository for the examples above.

Web resources

Emacs configuration

  • GitHub repository for configuring Emacs:
  • Basic configuration files using straight.el and use-package:

Files used in the examples

  • GitHub repository with the source files for the examples:

Official documentation

Media used in the post

  • Post cover image: Modified from
  • Artist: Pixabay
  • License: