Observable Notebooks 2.0 System guide
Notebooks are interactive documents comprised of small programs called cells. Cells can be written in JavaScript, Markdown, HTML, and other languages. Most logic is coded in JavaScript, while prose is typically written in Markdown.
const language = "JavaScript";
display(`This cell is ${language}.`);
This cell is Markdown.
This cell is Markdown .
And this cell is <i>HTML</i>.
Notebooks are live: code runs when you load the notebook, showing the latest output. A saved notebook only stores cells’ source code, not its output. (Though you can capture and store additional data in file attachments.)
This cell ran at ``.
This cell ran at
${(new Date).toLocaleString("se")} .
Notebooks are reactive: cells run automatically like in a spreadsheet, re-running whenever referenced variables change. Reactivity makes it easier to implement interactive controls (or inputs), animations, and asynchronous operations such as loading data.
const x = view(Inputs.range([0, 100], {label: "x", step: 1}));
const y = 2;
x ** y
Top-level variables defined in JavaScript cells, such as x
and y
above, can be referenced in other cells. For Markdown, HTML, and other non-JavaScript cells, use dollar-curly expressions ${…}
to interpolate dynamic values.
The value of .
The value of ${tex
${x}^${y} = ${(x ** y).toLocaleString().replace(/,/g, "\\,")} }.
JavaScript
Use JavaScript cells to render charts, inputs, and other dynamic content. JavaScript cells can also declare top-level variables, say to load data or declare helper functions.
JavaScript cells come in two forms: expression cells and program cells. An expression cell contains a single JavaScript expression (note the lack of semicolon) and implicitly displays its value:
1 + 2
A program cell, in contrast, contains statements (typically with semicolons) and declarations, and doesn’t display anything by default; however, you can call display
to display something:
const foo = 1 + 2;
display(foo);
When displaying — either implicitly or explicitly — if the displayed value is a DOM node, it will be inserted directly into the page. Anything else is displayed using the inspector, which is useful for debugging. You can use the DOM API to create content, though we recommend a library such as Hypertext Literal, Observable Plot, or D3.
function greeting(name) {
return html`Hello, <i>${name}</i>!`;
}
greeting("world")
Plot.lineY(AMZN, {x: "Date", y: "Close"}).plot({y: {grid: true}})
When a top-level variable is defined as a promise, referencing cells will implicitly await the promise and only see the resolved value.
const delayed = new Promise((resolve) => setTimeout(() => resolve("hello"), 10000));
delayed
When a top-level variable is defined as a generator, referencing cells will see only the latest yield value and will re-run whenever the generator yields a new value. This is typically used for interaction, animation, and live data.
const tick = (async function* () {
do {
yield new Date();
await new Promise((resolve) => setTimeout(resolve, 1000));
} while (true);
})();
tick
The built-in view
function displays the given input and returns a generator that yields the current value of the input.
const name = view(Inputs.text({label: "Name"}));
Hello, !
Hello, ${name || "anonymous"}!
The built-in FileAttachment
function references a local file. It is typically used to load data. For security purposes, you can only load local files that live in the same directory, or a subdirectory, of the current notebook.
const AMZN = FileAttachment("ex/data/AMZN.csv").csv({typed: true});
Inputs.table(AMZN)
To load live data, you can use fetch
, WebSocket
, and other standard web APIs.
const world = fetch("https://cdn.jsdelivr.net/npm/world-atlas/land-110m.json")
.then((response) => response.ok ? response.json() : Promise.reject(response.status))
.then(display);
The notebook standard library includes a handful of additional built-ins:
display
- a function to display a valueview
- a function to display an input and return its value generatorinvalidation
- a promise for disposing resources when a cell is re-runvisibility
- a promise to wait for a cell to become visiblewidth
- the current page widthnow
- the current time
A handful of recommended libraries (and sample datasets) are also available by default, including:
Inputs
- Observable Inputshtl
,html
,svg
- Observable Hypertext LiteralPlot
- Observable Plotd3
- D3.jstex
- a tagged template literal formd
- a tagged template literal for Markdownpenguins
- the Palmer Archipelago penguins dataset
You can load additional libraries from npm using an npm:
import
.
import he from "npm:he";
display(he.encode("foo © bar ≠ baz 𝌆 qux"));
Markdown
Markdown cells are typically used for prose, images, and other static content. Markdown is CommonMark-compliant, supports HTML in Markdown, automatic linkification, automatic typographic enhancements such as curly quotes and dashes, and automatic linking of headings.
Use dollar-curly ${…}
expressions to interpolate dynamic JavaScript values into Markdown, including text, DOM nodes, and bits of dynamic Markdown.
Hello, !
Hello, ${atob("d29ybGQ=")}!
HTML
HTML cells are used similarly to Markdown cells, and also support dollar-curly ${…}
interpolation. Unlike Markdown cells, HTML cells automatically escape any interpolated values, providing additional safety when handling dynamic and user-specified content.
Interpolated values are considered text, ${"not <i>HTML</i>"}.