Create, edit, and build Observable Notebooks using Notebook Kit. Use when working with .html notebook files, generating static sites from notebooks, querying databases from notebooks, or using data loaders (Node.js/Python/R) in notebooks. Covers notebook file format, cell types, CLI commands, database connectors, and JavaScript API.
Observable Notebook Kit is an open-source CLI and Vite plugin for building static sites from Observable Notebooks.
Notebooks are .html files with this structure:
<!doctype html>
<notebook theme="air">
<title>My Notebook</title>
<script type="text/markdown">
# Hello
</script>
<script type="module" pinned>
1 + 2
</script>
</notebook>
<notebook> Attributestheme - colorway: air (default), coffee, cotton, deep-space, glacier, ink, midnight, near-midnight, ocean-floor, parchment, slate, stark, sun-fadedreadonly - disallow editing<script>type - cell language (required)pinned - show source codehidden - suppress displayoutput - variable name to expose (for non-JS cells)database - database name (SQL cells)format - output format (data loader cells)id - cell identifiertype values)| Type | Language |
|---|---|
module | JavaScript |
text/x-typescript | TypeScript |
text/markdown | Markdown |
text/html | HTML |
application/sql | SQL |
application/x-tex | TeX/LaTeX |
text/vnd.graphviz | DOT (Graphviz) |
application/vnd.node.javascript | Node.js data loader |
text/x-python | Python data loader |
text/x-r | R data loader |
Use 4 spaces indentation inside <script> (auto-trimmed). Escape </script> as <\/script>.
Install: npm add @observablehq/notebook-kit
notebooks preview --root docs
notebooks preview --root docs --template docs/custom.tmpl
notebooks build --root docs -- docs/*.html
Output goes to .observable/dist.
Convert Observable notebook URL to local HTML:
notebooks download https://observablehq.com/@d3/bar-chart > bar-chart.html
Run database query manually:
notebooks query --database duckdb 'SELECT 1 + 2'
Expression cell (implicit display):
1 + 2
Program cell (explicit display):
const foo = 1 + 2;
display(foo);
display(value) - render value to cell outputview(input) - display input, return value generatorFileAttachment("path") - load local files (same/sub directory only)invalidation - promise for cleanup on re-runvisibility - promise when cell visiblewidth - current page widthnow - current timeInputs - Observable InputsPlot - Observable Plotd3 - D3.jshtl, html, svg - Hypertext Literaltex, md - tagged template literalsimport lib from "npm:package-name";
import {fn} from "jsr:@scope/package";
Top-level variables trigger re-runs. Promises auto-await; generators yield latest value.
const x = view(Inputs.range([0, 100]));
x ** 2 // re-runs when x changes
Use ${...} in Markdown/HTML cells:
The answer is ${x * 2}.
See references/databases.md for full configuration.
SQL cells use database attribute. Default is duckdb (in-memory).
<script type="application/sql" database="duckdb" output="results">
SELECT * FROM 'data.parquet'
</script>
@duckdb/node-api)postgres)snowflake-sdk)@google-cloud/bigquery)@databricks/sql)Install drivers separately: npm add @duckdb/node-api
.observable/cacheconst db = DatabaseClient("duckdb");
const data = await db.sql`SELECT * FROM t WHERE x = ${value}`;
Cells that run at build time via interpreter. Output cached in .observable/cache.
Text: text, json, csv, tsv, xml
Binary: arrow, parquet, blob, buffer
Image: png, jpeg, gif, webp, svg
Other: html
<script type="application/vnd.node.javascript" format="json" output="data">
process.stdout.write(JSON.stringify({hello: "world"}));
</script>
Requires Node.js 22.12+. Sandboxed with read-only access to notebook directory.
<script type="text/x-python" format="text" output="result">
print("hello", end="")
</script>
Requires Python 3.12+. Uses .venv if present.
<script type="text/x-r" format="json" output="data">
cat(jsonlite::toJSON(list(x = 1:3)))
</script>
Custom templates wrap built notebooks:
<!doctype html>
<html>
<head>
<style>@import url("observable:styles/index.css");</style>
</head>
<body>
<main></main> <!-- notebook renders here -->
</body>
</html>
Use --template path.tmpl with CLI.
Typical package.json:
{
"dependencies": {
"@observablehq/notebook-kit": "^"
},
"scripts": {
"docs:preview": "notebooks preview --root docs",
"docs:build": "notebooks build --root docs -- docs/*.html"
}
}
.observable/databases.json - database configs (keep out of git).observable/cache/ - query/data loader results.observable/dist/ - built siteRecommended .gitignore:
.observable