Observable Framework 1.9.0 GitHub️ 2.2k


A observablehq.config.js (or observablehq.config.ts) file located in the project root allows configuration of your project. For example, a site might use a config file to set the project’s title and the sidebar contents:

export default {
  title: "My awesome project",
  pages: [
    {name: "Getting ever more awesome", path: "/getting-awesome"},
    {name: "Being totally awesome", path: "/being-awesome"},
    {name: "Staying as awesome as ever", path: "/staying-awesome"}

Configuration files are optional. Any options you don’t specify will use the default values described below.

Configuration files are code. This means they can be dynamic, for example referencing environment variables. The configuration is effectively baked-in to the generated static site at build time. During preview, you must restart the preview server for changes to the configuration file to take effect.

The following options are supported.


The path to the source root; defaults to src. (Prior to , the default was docs.)


The path to the output root; defaults to dist.


The theme name or names, if any; defaults to default. Themes affect visual appearance by specifying colors and fonts, or by augmenting default styles. The theme option is a shorthand alternative to specifying a custom stylesheet.

To force light mode:

theme: "light"

To force dark mode:

theme: "dark"

For dashboards, to compose the default light and dark themes with alt and wide:

theme: "dashboard"

Or more explicitly:

theme: ["air", "near-midnight", "alt", "wide"]

You can also apply a theme to an individual page via the front matter:

theme: [glacier, slate]

See the list of available themes for more.


The path to a custom stylesheet, relative to the source root (typically src). This option takes precedence over the theme option (if any), providing more control by allowing you to remove or alter the default stylesheet and define a custom theme.

The custom stylesheet should typically import observablehq:default.css to build on the default styles. You can also import any of the built-in themes. For example, to create a stylesheet that builds up on the air theme, create a custom-style.css file in the source root, then set the style option to custom-style.css:

@import url("observablehq:default.css");
@import url("observablehq:theme-air.css");

:root {
  --theme-foreground-focus: green;

The default styles are implemented using CSS custom properties. These properties are designed to be defined by themes or custom stylesheets. The following custom properties are supported:

A custom stylesheet can be applied to an individual page via the front matter:

style: custom-style.css

In this case, the path to the stylesheet is resolved relative to the page’s Markdown file rather than the source root.


The project’s title. If specified, this text is used for the link to the home page in the sidebar, and to complement the titles of the webpages. For instance, a page titled “Sales” in a project titled “ACME, Inc.” will display “Sales | ACME, Inc.” in the browser’s title bar. If not specified, the home page link will appear as “Home” in the sidebar, and page titles will be shown as-is.

Whether to show the sidebar. Defaults to true if pages is not empty.


An array containing pages and sections. If not specified, it defaults to all Markdown files found in the source root in directory listing order.

Both pages and sections have a name, which typically corresponds to the page’s title. The name gets displayed in the sidebar. Sections are used to group related pages; each section must specify an array of pages. (Sections can only contain pages; nested sections are not currently supported.)

Clicking on a page in the sidebar navigates to the corresponding path, which should start with a leading slash and be relative to the root; the path can also be specified as a full URL to navigate to an external site. A section may specify a path to navigate to when the section header is clicked; if a section does not specify a path, then clicking the section header toggles the section (if collapsible; see below).

For example, here pages specifies two sections and a total of five pages:

export default {
  pages: [
      name: "Section 1",
      path: "/s01/",
      pages: [
        {name: "Page 1", path: "/s01/page1"},
        {name: "Page 2", path: "/s01/page2"}
      name: "Section 2",
      open: false,
      pages: [
        {name: "Page 3", path: "/s02/page3"},
        {name: "Page 4", path: "/s02/page4"}

Sections may be collapsible. If the open option is set, the collapsible option defaults to true; otherwise it defaults to false. If the section is not collapsible, the open option is ignored and the section is always open; otherwise, the open option defaults to true. A section will open automatically if the current page belongs to that section.

Pages and sections may also have a pager field which specifies the name of the page group; this determines which pages are linked to via the previous and next pager buttons. If the pager field is not specified, it defaults the current section’s pager field, or to main for top-level pages and sections. (The home page is always in the main pager group.) The pager field can be also set to null to disable the pager on a specific page or section, causing adjacent pages to skip the page.

Projects can have “unlisted” pages that are not referenced in pages. These pages can still be linked from other pages or visited directly, but they won’t be listed in the sidebar or linked to via the previous & next pager links.

The pages list should not include the home page (/) as this is automatically linked at the top of the sidebar. We also do not recommend listing the same page multiple times (say with different query parameters or anchor fragments), as this causes the previous & next pager links to cycle.


Whether to show the previous & next links in the footer; defaults to true. The pages are linked in the same order as they appear in the sidebar.

An HTML fragment to add to the head. Defaults to the empty string. If specified as a function, receives an object with the page’s title, (front-matter) data, and path, and must return a string.

An HTML fragment to add to the header. Defaults to the empty string. If specified as a function, receives an object with the page’s title, (front-matter) data, and path, and must return a string.

An HTML fragment to add to the footer. Defaults to “Built with Observable.” If specified as a function, receives an object with the page’s title, (front-matter) data, and path, and must return a string.

For example, the following adds a link to the bottom of each page:

footer: ({path}) => `<a href="https://github.com/example/test/blob/main/src${path}.md?plain=1">view source</a>`,


The base path when serving the site. Currently this only affects the custom 404 page, if any.


Whether page links should be “clean”, i.e., formatted without a .html extension. Defaults to true. If true, a link to config.html will be formatted as config. Regardless of this setting, a link to an index page will drop the implied index.html; for example foo/index.html will be formatted as foo/.


The table of contents configuration.

The following TypeScript interface describes this option:

export interface TableOfContents {
  show?: boolean;
  label?: string;

If show is not set, it defaults to true. If label is not set, it defaults to “Contents”. The toc option can also be set to a boolean, in which case it is shorthand for toc.show.

If shown, the table of contents enumerates the second-level headings (H2 elements, such as ## Section name) on the right-hand side of the page. The currently-shown section is highlighted in the table of contents.

The table of contents configuration can also be set in the page’s YAML front matter. The page-level configuration takes precedence over the project-level configuration. For example, to disable the table of contents on a particular page:

toc: false

If true, enable search on the project; defaults to false. The search option may also be specified as an object with an index method , in which case additional results can be added to the search index. Each result is specified as:

interface SearchResult {
  path: string;
  title: string | null;
  text: string;
  keywords?: string;

These additional results may also point to external links if the path is specified as an absolute URL. For example:

export default {
  search: {
    async* index() {
      yield {
        path: "https://example.com",
        title: "Example",
        text: "This is an example of an external link."


The interpreters option specifies additional interpreted languages for data loaders, indicating the file extension and associated interpreter. (See loader routing for more.) The default list of interpreters is:

  ".js": ["node", "--no-warnings=ExperimentalWarning"],
  ".ts": ["tsx"],
  ".py": ["python3"],
  ".r": ["Rscript"],
  ".R": ["Rscript"],
  ".rs": ["rust-script"]
  ".go": ["go", "run"],
  ".java": ["java"],
  ".jl": ["julia"],
  ".php": ["php"],
  ".sh": ["sh"],
  ".exe": []

Keys specify the file extension and values the associated command and arguments. For example, to add Perl (extension .pl) and AppleScript (.scpt) to the list above:

export default {
  interpreters: {
    ".pl": ["perl"],
    ".scpt": ["osascript"]

To disable an interpreter, set its value to null. For example, to disable Rust:

export default {
  interpreters: {
    ".rs": null


A hook for registering additional markdown-it plugins. For example, to use markdown-it-footnote, first install the plugin with either npm add markdown-it-footnote or yarn add markdown-it-footnote, then register it like so:

import MarkdownItFootnote from "markdown-it-footnote";

export default {
  markdownIt: (md) => md.use(MarkdownItFootnote)


If true, enables simple typographic replacements in Markdown, such as replacing (c) with © and converting straight quotes to curly quotes. See also the quotes option, which should be set for non-English languages if the typographer option is enabled. For the full list of replacements, see markdown-it. Defaults to false.


The set of replacements for straight double and single quotes used when the typographer option is enabled. Defaults to ["“", "”", "‘", "’"] which is suitable for English. For example, you can use ["«", "»", "„", "“"] for Russian, ["„", "“", "‚", "‘"] for German, and ["«\xa0", "\xa0»", "‹\xa0", "\xa0›"] for French.


If true (the default), automatically convert URL-like text to links in Markdown.