mystmd Documentation

Command line tools for working with MyST Markdown.

527 stars TypeScript TypeScript 1 file · ~125,262 tokens #javascript-parser#markdown-it#myst#myst-markdown#myst-parser#unifiedjs
RAW Doc

Technical Documentation: jupyter-book/mystmd

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (`README.md`)

MyST Markdown Command Line Interface, `mystmd`

mystmd is a set of open-source, community-driven tools designed for scientific communication, including a powerful authoring framework that supports blogs, online books, scientific papers, reports and journals articles.

Overview

The mystmd project provides a command line tool (mystmd) for working with MyST Markdown projects.

  • Provides functionality for cross-referencing, external structured links, and scientific citations
  • Translate and render MyST Markdown into:
    • HTML for static websites, and modern React for interactive websites (like this website!)
    • PDFs and LaTeX documents, with specific templates for over 400 journals
    • Microsoft Word export
  • Parse MyST into a standardized AST, that follows the MyST Markdown Spec

See the documentation.

Get Started

Ensure that you have an updated version of Node installed (https://nodejs.org/):

bash
node -v
>> v20.4.0

The MyST Markdown CLI is available through NPM, PyPI and Conda:

bash
# Using npm, yarn, or pnpm
npm install mystmd
# Or using PyPI
pip install mystmd
# Or using Conda / Mamba
conda install mystmd -c conda-forge

Usage:

bash
myst init
myst start
myst build my-doc.md --tex

Development

See the Contribution Guide for information on setting up a development environment.


As of v1.0.0 this package is ESM only.



Overview (`.changeset/README.md`)

Changesets

Hello and welcome! This folder has been automatically generated by @changesets/cli, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it in our repository

We have a quick list of common questions to get you started engaging with this project in
our documentation


Overview (`docs/README.md`)

Contribute to the documentation

This folder contains the content for the MyST Guide at mystmd.org/guide.

See the documentation contribution guide for more information.


Overview (`packages/jats-to-myst/README.md`)

jats-to-myst

Convert a JATS XML document to MyST AST


Overview (`packages/jtex/README.md`)

jtex

jtex is a command line tool (CLI) for rendering LaTeX documents from jinja-style templates. This package uses Jinja2 as the template engine with a modified environment and syntax that plays well with LaTeX's markup.

Installation

Install the package into your virtual environment using pip:

bash
npm install jtex

and confirm correct installation by typing:

bash
jtex --version

Overview (`packages/myst-cli-utils/README.md`)

myst-cli-utils

Simple utilities for myst CLI tools.


Overview (`packages/myst-cli/README.md`)

myst-cli

See the docs.


Overview (`packages/myst-common/README.md`)

myst-common

Common utilities and types for MyST.


Overview (`packages/myst-directives/README.md`)

myst-directives

Default directive definitions for MyST parser.

Directives

These directives are ported from markdown-it-docutils:

  • admonitions
  • code / code-block
  • math
  • image
  • list-table
  • figure

Overview (`packages/myst-execute/README.md`)

MyST Execute

Kernel execution of code and inline expressions using Jupyter.

This package provides a transform that takes a MyST tree, computes the executable content, and stores the results in the AST. It provides basic support for caching of outputs to avoid needless computation.


Overview (`packages/myst-ext-button/README.md`)

myst-ext-button

mystmd extension for button role


Overview (`packages/myst-ext-card/README.md`)

myst-ext-card

mystmd extension for card directive


Overview (`packages/myst-ext-exercise/README.md`)

myst-ext-exercise

mystmd extension for exercise directive


Overview (`packages/myst-ext-grid/README.md`)

myst-ext-grid

mystmd extension for grid directive


Overview (`packages/myst-ext-icon/README.md`)

myst-ext-icon

mystmd extension for icon role


Overview (`packages/myst-ext-proof/README.md`)

myst-ext-proof

mystmd extension for proof directive


Overview (`packages/myst-ext-reactive/README.md`)

myst-ext-card

mystmd extension for card directive


Overview (`packages/myst-ext-tabs/README.md`)

myst-ext-tabs

mystmd extension for tab-set and tab-item directives


Overview (`packages/myst-migrate/README.md`)

myst-migrate

Utilities for upgrading and downgrading MyST ASTs


Overview (`packages/myst-parser/README.md`)

myst-parser

MyST (Markedly Structured Text) is designed to create publication-quality documents written entirely in Markdown. The main use case driving the development and design of MyST is JupyterBook, which helps you create educational online textbooks and tutorials with Jupyter Notebooks and narrative content written in MyST. myst-parser is a javascript parser for MyST markdown that brings these capabilities into a web-native environment.

Goals

  • Provide a Javascript implementation of MyST markdown
  • Parse MyST into a standardized AST, based on mdast.
  • Serialize mdast into a default HTML for all known roles and directives
  • Expose an opinionated set of markdown-it plugins, to be used in ecosystems that require markdown-it (e.g. vscode)
  • Expose extension points in MyST for new roles/directives
  • Provide functionality for cross-referencing that is usually completed by Sphinx (e.g. in the Python implementation)

Usage

bash
npm install myst-parser

In a node environment:

javascript
import { mystParser } from 'myst-parser';
import { State, transform, mystToHast, formatHtml } from 'myst-to-html';
import rehypeStringify from 'rehype-stringify';
import { unified } from 'unified';

const pipe = unified()
  .use(mystParser)
  .use(transform, new State())
  .use(mystToHast)
  .use(formatHtml)
  .use(rehypeStringify);

const file = pipe.processSync(':::{important}\nHello to the world!\n:::');
console.log(file.value);
>> <aside class="admonition important"><p class="admonition-title">Important</p><p>Hello to the world!</p></aside>

In a browser:

html
<html>
  <head>
    <script src="https://unpkg.com/myst-parser"></script>
  </head>
  <body onload="init();">
    <div id="output"></div>
    <script>
      function init() {
        const pipe = unified()
          .use(mystParser)
          .use(transform, new State())
          .use(mystToHast)
          .use(formatHtml)
          .use(rehypeStringify);
        const result = pipe.runSync('# Hello to the world!');
        const html = pipe.stringify(result);
        document.getElementById('output').innerHTML = html;
      }
    </script>
  </body>
</html>

`myst-parser` Features

  • CommonMark
  • Admonitions
  • Figures
  • Images
  • Math
    • role
    • directive (equations)
    • dollar math
    • amsmath
  • Tables
    • GFM
    • List Tables
  • References
    • ref
    • numref
    • eq
    • links
    • Including numbering (single document)
  • Code Directives
    • Code
    • Code blocks
    • Code cell
  • Blocks
  • Comments
  • Targets
  • HTML:
    • sub
    • sup
    • abbr
  • Definition List
  • Footnotes

Not yet complete:

  • div
  • proof
  • margin
  • sidebar
  • colon fence
  • Citations
  • Bibliography
  • Epigraph
  • Glosary
  • Terms
  • Tabs
  • Panels
  • CSV Tables
  • Multi-document

Developer Install

For installing the package locally, you will need node and npm, both can use a global install on your system.

Once you have npm installed globally, navigate into this project folder and install the dependencies:

bash
cd myst-parser
npm install
npm run start  # Start a development server to play with the library! 🚀

The scripts for building, testing, and serving the project are in the package.json, the main ones to use are
npm run test, npm run build, and npm run start.

`npm run build`

Builds the library, including compiling the typescript and bundling/minification to create myst.min.js which can be used in the browser directly.
This outputs to the dist folder, and also includes all type definitions (*.d.ts) in the types folder.

`npm run test`

Run the tests, these are mostly based on the fixtures folder. You can also use npm run test:watch to run on any file changes.

`npm run start`

Starts a server for manually testing and playing with myst-parser, this uses a in-memory bundle of what would go in the dist folder.
Note that this does not actually build the library!


Overview (`packages/myst-roles/README.md`)

myst-roles

Default role definitions for MyST parser.

Roles

These roles are ported from markdown-it-docutils:

  • sup/superscript
  • sub/subscript
  • abbr/abbreviation
  • eq
  • ref
  • numref
  • math

Overview (`packages/myst-spec/README.md`)

MyST Spec

:warning: The MyST AST specification is still in dev; any structures or features present in the JSON schema may change at any time without notice.

MyST (Markedly Structured Text) is designed to create publication-quality, computational documents written entirely in Markdown. The main use case driving the development and design of MyST is JupyterBook, which creates educational online textbooks and tutorials with Jupyter Notebooks and narrative content written in MyST.

MyST is a superset of CommonMark (a standard form of Markdown) and allows you to directly create “directives” and “roles” as extension points in the language. These extensions points are influenced by ReStructured Text (RST) and Sphinx -- pulling on the nomenclature and introducing additional standards where appropriate. directives are block-level extension points, like callout panels, tabs, figures or embedded charts; and roles are inline extension points, for components like references, citations, or inline math.

md
```{directive} Argument
The *content* of a directive can have {role}`with content`
```

Goals

myst-spec is designed to standardize implementations and extensions to MyST, with the goal of making the MyST ecosystem as rich and interoperable as possible. The spec formalizes three formats:

  1. the MyST markup syntax, to ensure MyST works as expected across languages and implementations;
  2. the MyST abstract syntax tree (AST), to promote an ecosystem of transformations and exports to diverse formats (e.g. latex/word/html/docutils/etc.); and
  3. suggested semantic HTML output and CSS class structure, to promote web-accessibility and interoperability of themes.

The myst-spec will be improved overtime through enhancement proposals which can be submitted by our multi-stakeholder community. [IN PROGRESS]

MyST Spec

This repository introduces tests to cover the MyST Spec. These tests cover three target formats: myst, mdast, and html. An simple example test case (in yaml) for a header looks like:

yaml
cases:
  - title: CommonMark headers
    myst: |-
      # Heading!
    mdast:
      type: root
      children:
        - type: heading
          depth: 1
          children:
            - type: text
              value: Heading!
    html: |-
      <h1>Heading!</h1>

[TBD: Do we include latex here, maybe?]

Markdown Abstract Syntax Tree, `mdast`

Markdown AST, or mdast, is an intermediate format that builds upon the existing mdast spec, which is used throughout the unifiedjs Javascript community with hundreds of existing transformations, utilities and serializers. mdast is simple, with a type defining the node optional properties on the node and optional children (a leaf node has a value).

Beyond CommonMark and GitHub Flavoured Markdown, MyST introduces new directives and roles (like admonitions, citations, equations) following existing standards where they are defined. mdast is serializable to JSON or YAML, and can be effectively shared between projects, languages and implementations. The output of this repository is a versioned JSON file that can be used in implementations of MyST.


Overview (`packages/myst-to-docx/README.md`)

`myst-to-docx`

Export a MyST document to a Microsoft Word file, using docx.

Overview

myst-to-docx has a DocxSerializer object that you write to as you walk the MyST document. It is a light wrapper around https://docx.js.org/, which actually does the export. myst-to-docx is write only (i.e. can export to, but can’t read from *.docx), and has all standard MyST nodes covered (see below).

myst-to-docx can be used in the browser or in node. This library currently only has dependence on docx, myst-frontmatter and buffer-image-size - and the serialization handlers can be edited externally (see Extended Usage below).

The AST should be transformed through myst-transforms to ensure that all nodes are enumerated.

Basic usage in browser

The utility fetchImagesAsBuffers walks the AST and downloads images into buffers to be used by docx, it also figures out the size and returns an object with getImageBuffer and getImageDimensions, which need to be passed to the options. If your images are more complex (e.g. they are mermaid diagrams, or Jupyter Outputs), you will need to do more complex preprocessing to create these two functions.

A Blob is returned, which can be downloaded client side.

typescript
import { unified } from 'unified';
import { mystToDocx, fetchImagesAsBuffers, DocxResult } from 'myst-to-docx';

const opts = await fetchImagesAsBuffers(tree);
const file = unified().use(mystToDocx, opts).stringify(tree);
const blob = await (file.result as DocxResult);

Basic usage in node

In node, the image dimensions are discovered using a node-only package, and don't need to be provided. The result of the conversion in node is a Buffer as the file.result, which can be saved to disk.

typescript
import { unified } from 'unified';
import { mystToDocx, DocxResult } from 'myst-to-docx';

const file = unified()
  .use(mystToDocx, {
    getImageBuffer(url) {
      return fs.readFileSync(url).buffer;
    };
  })
  .stringify(tree);
const buffer = await (file.result as DocxResult);

Extended usage

Instead of using the defaultDocxSerializer you can override or provide cusome serializers.

ts
import { DocxSerializer, defaultNodes, defaultMarks } from 'myst-to-docx';

const nodeSerializer = {
  ...defaultNodes,
  my_paragraph(state, node) {
    state.renderInline(node);
    state.closeBlock(node);
  },
};

export const myDocxSerializer = new DocxSerializer(nodeSerializer, defaultMarks);

The state is the DocxSerializerState and has helper methods to interact with docx.

Supported Block Nodes

  • text
  • link
  • paragraph
  • heading (levels)
    • Including numbering of headings
  • blockquote
  • code
    • TODO: No styles supported
  • thematicBreak
  • break
  • list
  • listItem
  • image
  • math
    • Including numbering
  • inlineMath
  • crossReference
  • abbreviation
  • block
  • definitionList
  • definitionTerm
  • definitionDescription
  • container
  • caption
  • captionNumber
  • tables

Supported Style Nodes

  • emphasis
  • strong
  • inlineCode
  • subscript
  • superscript
  • delete (strikethrough)
  • underline
  • smallcaps

Resources


Overview (`packages/myst-to-html/README.md`)

myst-to-html

Convert a MyST AST to HTML.

typescript
import { u } from 'unist-builder';
import { mystToHtml } from 'myst-to-html';

const html = mystToHtml(u('root', [u('paragraph', [u('text', 'hello world')])]));
// '<p>hello world</p>'

Overview (`packages/myst-toc/README.md`)

myst-toc

Utilities to parse a MyST table of contents.

Overview

The MyST ToC format is defined in types.ts, from which a JSON schema definition is compiled. The high-level description is as follows:

  1. A TOC comprises of an array of TOC items
  2. Each TOC item can be
  • A file (document)
  • A URL (document)
  • A collection of child items
  1. TOC items containing children must have either a title or a document

Example

Example myst.yml under the toc: key:

yaml
toc:
  - title: Main
    file: main.md
  - title: Overview
    children:
      - file: overview-1.md
      - file: overview-2.md
  - url: https://google.com
  - file: getting-started.md
    children:
      - file: getting-started-part-1.md
      - file: getting-started-part-2.md

Overview (`packages/myst-transforms/README.md`)

myst-transforms

unifiedjs transforms for working with MyST Markdown documents.

Overview

These transforms take AST from mystmd and turn it into usable forms, enforce document structure, or provide other utilities.

These utilities are available on npm:

bash
npm install myst-transforms

You can use the plugins as follows:

typescript
import { unified } from 'unified';
import { mathPlugin } from 'myst-transforms';

unified()
  .use(mathPlugin, { macros: {} }) // Add the plugin with any options
  .run(tree); // Run the AST through the set of plugins

// The AST has now been modified in place.

Overview (`packages/mystmd-py/README.md`)

MyST Markdown Command Line Interface, `mystmd`

mystmd is a set of open-source, community-driven tools designed for scientific communication, including a powerful authoring framework that supports blogs, online books, scientific papers, reports and journals articles.

Note
The mystmd project is in beta. It is being used to explore a full MyST implementation and will change significantly and rapidly.
The project is being developed by a small team of people on the Executable Books Project, and may make rapid decisions without fully public/inclusive discussion.
We will continue to update this documentation as the project stabilizes.

Overview

The mystmd project provides a command line tool (mystmd) for working with MyST Markdown projects.

  • Provides functionality for cross-referencing, external structured links, and scientific citations
  • Translate and render MyST Markdown into:
    • HTML for static websites, and modern React for interactive websites (like this website!)
    • PDFs and LaTeX documents, with specific templates for over 400 journals
    • Microsoft Word export
  • Parse MyST into a standardized AST, that follows the MyST Markdown Spec

See the documentation.

Get Started

The MyST Markdown CLI is available through PyPI:

bash
pip install mystmd
myst init
myst build my-doc.md --tex

and Conda:

bash
conda config --add channels conda-forge
conda config --set channel_priority strict
conda install mystmd
myst init
myst build my-doc.md --tex

Overview (`packages/mystmd/README.md`)

mystmd

The MyST Markdown Command Line interface.

See https://mystmd.org


Overview (`packages/simple-validators/README.md`)

simple-validators

Schema validation functions and error reporting framework

Scope

This package provides basic validation functions for building more complex schema validation. It is explicit and verbose with no magic to infer anything from existing types. Rather than throwing validation errors, these functions pass error/warning messages out as options and return undefined; the consumer may then choose how to handle error reporting.

Usage

text
/* Detailed source-code truncated for AI context efficiency. */

Overview (`packages/tsconfig/README.md`)

`tsconfig`

These are base shared tsconfig.jsons from which all other tsconfig.json's inherit from.


Overview (`packages/myst-common/scripts/README.md`)

MyST Scripts

This directory contains utility scripts for maintaining the MyST codebase.

generate-rule-severities.mjs

Automatically generates packages/myst-common/src/rule-severities.ts by analyzing the codebase for rule usage patterns.

What it does

The script:

  1. Scans all TypeScript files in the packages directory
  2. Searches for patterns like:
    • fileError(..., { ruleId: RuleId.* }) → error severity
    • fileWarn(..., { ruleId: RuleId.* }) → warn severity
    • addWarningForFile(..., 'error', { ruleId: RuleId.* }) → error severity
    • addWarningForFile(..., 'warn', { ruleId: RuleId.* }) → warn severity
    • *ValidationOpts(..., RuleId.*) → error severity (uses both, but errors are critical)
  3. Aggregates the severities for each rule
  4. Generates the rule-severities.ts file with:
    • Default severity for each rule
    • Comments explaining where each rule is used
    • Proper categorization matching the RuleId enum

Usage

bash
# From the repository root
node scripts/generate-rule-severities.mjs

Overview (`packages/mystmd/tests/url-folders/readme.md`)

Readme

xref to subfolder

explicit xref to subfolder


Index (`docs/index.md`)


title: MyST Markdown Tools

{abbr}MyST (Markedly Structured Text) is an ecosystem of open-source, community-driven tools designed to revolutionize scientific communication. Our powerful authoring framework supports blogs, online books, scientific papers, reports and journals articles.

:::{card} Get Started With MyST 👩‍💻
:link: ./quickstart.md
Start here to get up and running with the myst command-line tools.
:::

Cool MyST Features 🪄

We think {abbr}MyST (Markedly Structured Text) is really cool, some of the features that we think stand out are highlighted below with links to places in the documentation where you can reproduce them!


🪄🐰 Rabbit-hole links allow you to get information to your reader as fast as possible, and they can deep-dive all the way to computations, code and interactive figures. You can play with this demo yourself in .

:::{figure} ./videos/links.mp4
:class: framed
:::

🪄📊 Live graphs can be embedded directly in your documentation or articles with computation backed by Jupyter or JupyterLite – running locally, on Binder, or directly in your browser. Get up and running with Thebe in

:::{figure} ./videos/thebe.mp4
:class: framed
:::

🪄🪐 JupyterLab support for MyST comes with inline computations, support for ipywidgets, matplotlib sparklines, editable task-lists, rich frontmatter, and beautiful typography and other elements like dropdowns, grids and cards. Install jupyterlab-myst today!

:::{figure} ./videos/jupyterlab-myst.mp4
:class: framed
:::

🪄📜 Export to PDF is easy with MyST, and we support hundreds of different journals out of the box, see myst-templates! You can also export to Microsoft Word or even JATS, which is used in scientific publishing. See the !

:::{figure} ./images/myst-build.png
:class: framed
:::


Quickstart Tutorials

:::{seealso}
:class: dropdown

Coming from Jupyter Book V1 or Sphinx?

👋 We are glad you are here! 💚

There are many ways that mystmd can be used with Jupyter Book and Sphinx. We recommend that you read a high-level overview of MyST as well as the project background, which goes over how these projects overlap and work together!

TL;DR
: Yes, you can use mystmd with your Jupyter Book! mystmd can create scientific PDFs and can natively read the _toc.yml as well as all of your existing MyST Markdown content and Jupyter Notebooks.
: Yes, mystmd is compatible with intersphinx even though it is written in JavaScript not Python!
: Jupyter Book and mystmd have overlap in the ability to create online books like this one. mystmd has some extra capabilities for cross-references, interactivity and performance.
: For more information see this upgrade guide from Jupyter Book v1 to v2.
:::

Cite `mystmd`

To cite or read more references about the mystmd project and related tools, see .

Project Goals

MyST is part of the Project Jupyter organization, and is an open-source, community-driven project to improve scientific communication, including integrations into Jupyter Notebooks and computational results.

::::{grid} 1 1 2 3

:::{card}
:link: ./citations.md

Built for Science 👩‍🔬
^^^

Extend Markdown with equations, cross-references, citations, and export to a preprint or rich, interactive website or book.
+++
MyST for Science »
:::

:::{card}
:link: ./interactive-notebooks.ipynb

Dynamic Documents 📈
^^^

Make your pages interactive by connecting to custom JupyterHubs, public Binders or even Python running directly in your browser.
+++
Bring your pages to life »
:::

:::{card}
:link: ./accessibility-and-performance.md

Fast & Accessible ⚡️
^^^
Publish next-generation articles and books that are beautifully designed, without compromising on accessibility or performance.
+++
Read about performance »
:::

::::

Technical Goals

Architecture

The mystmd command line tool can be used to parse MyST Markdown and Jupyter Notebooks into an AST. This data can be saved as JSON, or rendered to a website (like this one!) or any number of formats including PDF & $\LaTeX$, Word, React, or JATS.

{mermaid}
flowchart LR
  A[Jupyter Notebook] --> C
  B[MyST Markdown] --> C
  C(mystmd) --> D{AST}
  D <--> E[LaTeX]
  E --> F[PDF]
  D --> G[Word]
  D --> H[React]
  D --> I[HTML]
  D <--> J[JATS]
{important .dropdown} using sphinx or python?
For integration with **Sphinx**, use the Python implementation for MyST or Jupyter Book V1, which can be found at:

- [MyST Python Parser for Sphinx](https://myst-parser.readthedocs.io/en/latest/)
- [Jupyter Book](https://jupyterbook.org/)

Although many tools in the [MyST Ecosystem](https://mystmd.org) follow the same conventions and [specification](https://mystmd.org/spec), the following documentation refers only to the `mystmd` CLI.

Overview (`docs/overview.md`)

Overview of the MyST stack

This section provides a high-level overview of the main concepts and tools in the MyST ecosystem. See the guiding principles of the MyST ecosystem for some more high-level context.

(overview-build-process)=

Overview of the MyST build process

The MyST Build process takes input documents from authors and converts them into outputs that are meant for consumption by readers. The MyST CLI contains logic to use the MyST Document Engine, renderers, and themes to carry out this entire process. Here's a high-level breakdown of the major steps of that process:

{list-table}
:header-rows: 1
- - Phase
  - Input
  - Output
- - (1) **Writing**: An author writes content with {term}`MyST Markdown` in a file.
  - Ideas
  - Content (`.md`, `.ipynb`, `.tex`, etc)
- - (2) **Parsing**: A {term}`MyST Document Engine` parses content into structured data called the {term}`MyST AST`. See [](#overview-parsing).
  - Raw content (`.md`, `.ipynb`, etc)
  - Raw MyST AST
- - (3) **Resolving**: Transform raw MyST AST into AST that can be rendered. See [](#overview-transformers).
  - Raw MyST AST
  - Resolved / Processed MyST AST
- - (4) **Rendering**: A {term}`MyST Renderer` transforms the {term}`MyST AST` into components that can be used by a {term}`MyST Theme`.
  - Resolved MyST AST
  - Components that can be rendered into a final output with a _template_ and/or _theme_ (e.g., LaTeX, React, etc).
- - (5) **Theming / templating**: Rendered components are used by a {term}`MyST Theme` to generate final artifacts. See [](#overview-themes).
  - Template components that have been rendered by a MyST renderer.
  - Output formats that are ready for final consumption (e.g., a website or PDF).

Here is a brief overview of these steps:

% NOTE: This is an excalidraw SVG. You can edit it directly in Excalidraw.
% See the contributing docs for a short guide.
:::{figure} images/overview-diagram-build.svg
An overview of the "Parsing and Resolving" phase using the MyST Document Engine.
In this stage, a text file written in MyST Markdown is parsed by the {term}MyST Document Engine which outputs {term}MyST AST that follows the {term}MyST Specification. This file undergoes a number of transformations to add metadata, resolve links, and enrich the document. The final output is MyST AST (usually a .json file) that can be rendered into many different kinds of outputs.
:::

:::{figure} images/overview-diagram-render.svg
An overview of the "Rendering and Theming" phase using one or more MyST renderers / themes. In this stage, a resolved MyST AST is rendered into multiple components by a MyST renderer. These are building blocks that a theme knows how to convert into a final output. For example, the MyST React Renderer and the MyST React Themes know how to use these React components to create a final website.
:::

(overview-parsing)=

How does parsing work?

The MyST Document engine knows how to parse many kinds of documents into {term}MyST AST. This is most-commonly done with Markdown files (.md) or Jupyter Notebooks (.ipynb) written in {term}MyST Markdown, a flavor of Markdown that was designed for the MyST Document Engine.

However, the MyST Engine knows how to parse other kinds of syntax into MyST AST as well. For example, for admonition compatibility with GitHub Markdown. This is because we see the {term}MyST AST as the primary point of standardization for the MyST ecosystem, not the Markdown flavor. Input documents may have many different forms, but once it is parsed into {term}MyST AST, the document should have a standardized structure defined by the {term}MyST Specification.

What is the MyST AST and Specification?

The {term}MyST AST is a version of parsed MyST content that follows a specific structure so that Renderers can convert into different outputs. MyST AST conforms to the {term}MyST Specification. The {term}MyST Specification is a superset of the mdast specification (meaning that MyST includes all of the syntax that mdast supports, plus some extra ones like Directives).

Here's an example of MyST content, and the same content parsed as MyST AST:

{list-table}
:header-rows: 1
- - Content
  - AST
- - `**Hello there!**`
  - ```yaml
    - type: block
    children:
      - type: paragraph
        children:
          - type: strong
            children:
              - type: text
                value: Hello there!
    ```

The easiest way to understand how {term}MyST Markdown gets converted to {term}MyST AST is to explore the MyST Sandbox. Click the {kbd}AST tab to see the underlying AST structure for anything that you type. The {kbd}PRE tab represents the initial parsing phase, and the {kbd}POST tab represents the AST after the resolving phase.

If you're building your own renderer or parser, start with the AST primer. It walks through the node structure, the PRE/POST distinction, and what the spec formalizes vs. what's implementation-specific.

(overview-transformers)=

How do MyST Transformers work?

MyST Transformers are a way to convert a {term}MyST AST node into another type of node. Transformers operate on AST rather than on raw Markdown because AST has more standardized structure to work with.

For example, consider a Markdown link like [some text](#a-label). In MyST Markdown, this defines a cross-reference to #a-label, but it uses Markdown link syntax. We use a MyST Transformer to convert that Markdown to a cross-reference AST node like so:

  • First parse the Markdown [some text](#a-label).
  • The result is a MyST AST node for a Markdown link.
  • Next, search the document AST for any Markdown link nodes with a target that starts with #. Check if there's a target for the link. If so, it is meant to be a cross reference.
  • If so, converts the Markdown Link node into a Cross Reference node.

Some other uses for Transformers include:

  • Lifting metadata from code-cells to their parent structures
  • Check that figures have alt-texts
  • Convert non-standard AST nodes (e.g., ones generated from a custom user directive) into ones that MyST knows how to render[^ex-transform-node].

[^ex-transform-node]: For example, see this Executable Transform example from Project Pythia. It is an example of an executable transform, which takes custom pythia-cookbooks nodes and converts them (via some HTTP fetches) to a grid of cards by outputting the relevant grid and card AST nodes.

See for more details about how transforms operate on an AST and what intermediate steps look like.

(overview-themes)=

How do MyST themes and templates work?

MyST {term}themes and MyST {term}templates are both related to exporting a resolved MyST AST into one or more final output formats. They differ in the amount of customizability that a user has in altering their behavior, but the end-result is the same (generating an export artifact).

{glossary}
Templates
: Templates are usually used for [static exports](./documents-exports.md) (like LaTeX or Typst). The are structured like a final document (e.g., a LaTeX template for a specific journal), but with fields that MyST uses to insert document metadata at export time (e.g., a `doc.author` field if you want to insert the author name). See [the `myst-templates` GitHub repository](https://github.com/myst-templates) for a list of templates that you can use and contribute to.

Themes
: Themes expose more customizability than a template (which simply generates static outputs via a Jinja-like filter), but they function similar to a template because they take rendered components as inputs and export a final output. Themes are usually meant for _websites_, because there is a lot more control a website exposes compared to a static document like LaTeX. Themes have more complex logic, and thus their source-code is often in a dedicated repository. For examples, see the [build outputs of the `book` and `article` web themes are `myst-templates`](https://github.com/myst-templates#templates), but their [source code is in `myst-theme`](https://github.com/jupyter-book/myst-theme/tree/main/themes).

For a more technical explanation see the developer guide on themes, templates, and renderers.

Are there other MyST engines?

The MyST Specification and MyST Markdown Syntax was designed so that others could implement their own parsers, document engines, and renderers using MyST. Although there are presently no independent examples of a true {term}MyST Document Engine, the MyST Parser for Sphinx project fulfills a similar role in the Sphinx ecosystem.

The MyST Parser for Sphinx is an extension for Sphinx that can parse MyST Markdown into Sphinx's internal document structure. It was created to allow users to parse MyST Markdown syntax into Sphinx for V1 of Jupyter Book. It now exists as an independent extension for the Sphinx community, as Jupyter Book V2 now uses the MyST Document Engine.

:::{note} MyST Parser for Sphinx Compatibility
As the MyST Parser for Sphinx does not support all of the syntax defined in the MyST Guide, or output {term}MyST AST, it's not a true {term}MyST Document Engine as defined by this project. That's OK! Its goal is to be a useful tool for the Sphinx community that only leverages parts of the MyST ecosystem (the Markdown flavor), which for many people is "good enough."
:::

How does Jupyter Book relate to MyST?

The MyST Markdown Syntax was originally created to allow Jupyter Book V1 to use the Sphinx Document engine with Markdown content[^md], rather than the less well-known wiki:ReStructuredText language.

[^md]: Specifically, the MyST Parser for Sphinx was created to move beyond the functionality of the recommonmark project in order to natively support docutils roles and directives in Markdown.

Over time, the Jupyter Book team decided that the most sustainable path forward was to maintain its own document engine, the MyST Document Engine. An early version of the MyST Document Engine was created by Curvenote. To promote its long-term sustainability, the project was open sourced and further developed in collaboration with the Jupyter Book team. It serves as the engine behind Jupyter Book V2.

You can think of Jupyter Book as a distribution of the MyST Document Engine. In other words, Jupyter Book wraps the MyST Document Engine application, with out-of-the-box configuration that supports a multi-page community knowledge base or documentation site. MyST is created and maintained by the Jupyter Book team.

Currently, Jupyter Book and the MyST Document Engine share much of the same functionality. Over time, we imagine that the MyST Document Engine will be a more flexible tool that makes fewer assumptions about the end use-case that it's being used for. Jupyter Book will then have "more opinions" above the base configuration of the MyST Document Engine. That said — we are still figuring this out! We aren't sure the exact relationship the two will have, and will revisit this as we learn more. See next.jupyterbook.org for Jupyter Book V2 documentation, and see this section of the Contributing Guide for our approach to documenting the two projects.

How do MyST and Sphinx compare?

Sphinx is an open-source documentation system used in many software projects, especially in the Python ecosystem. It builds an internal representation of technical documents as a tree (see docutils) similar to the {term}MyST AST. Whilst Sphinx partially defines a specification of sorts[^docutils], a {term}MyST Document Engine explicitly publishes its {term}MyST AST for other tools to consume.

The Sphinx ecosystem has excellent support for Python documentation, referencing content, as well as externally providing an inventory of references known as intersphinx. You can link to Sphinx documentation with a {term}MyST Document Engine by using intersphinx references, and the official {term}MyST Document Engine automatically exposes the information required to allow Sphinx documentation to reference your MyST project.

At this time, the official {term}MyST Document Engine does not support the authoring of software API documentation[^api-docs]. As such, if your project is documenting Python software we suggest that you use Sphinx. If your project is primarily tutorials, educational textbooks (including with Jupyter Notebooks), a presentation, or scientific paper, we hope that you find a better fit with the MyST stack!

Sphinx and MyST take very different approaches to publishing to the web. In Sphinx, themes are used to customize the generation of HTML and JavaScript. Sites built using these themes can be deployed to static web servers like ReadTheDocs. In the MyST world, the equivalent to a Sphinx theme is a {term}MyST Renderer that consumes the {term}MyST AST and outputs HTML and JavaScript for your browser. Formally separating the {term}MyST Document Engine that generates {term}MyST AST and the {term}MyST Renderer that consumes it makes it possible to create rich new experiences with MyST, such as the SciPy Conference Proceedings website that renders {term}MyST AST on the fly.

Although the official engine also supports static HTML outputs, implementing themes as applications makes it easier to build more powerful MyST viewing experiences.

[^api-docs]: In the future, mystmd may offer support for Python and JavaScript documentation, and if you want to contribute please discuss on #1259 and this video.
[^docutils]: The Docutils source-code is the specification.


Quickstart (`docs/quickstart.md`)


title: Get Started subtitle: Your first steps with MyST subject: MyST Quickstart Tutorial

These quickstart tutorials are here to get you up and running on your local computer 👩‍💻.
See this overview of the MyST ecosystem for a more conceptual introduction. In this tutorial, you will get up and running from scratch. You will learn how to:

  • write MyST Markdown 🖊
  • export PDF, Word and $\LaTeX$ documents 📑
  • create a website like this one 🌎

:::{note} Goals and Prerequisites
Goal: The goal of this tutorial is to help you install MyST on your local computer 👩‍💻, create a local website 🌎, and edit elements of the theme to improve the website style 🎨.

Skills: It is helpful if you have some familiarity with using the command line, as well as using a text editor and/or JupyterLab.

Tools: You'll need a code and notebook editor (VSCode is great, and we recommend Jupyter Lab for notebooks) as well as access to a CLI.
:::

(lookout-for-tutorial-actions)=

🛠 Throughout the tutorial, whenever you're supposed to do something you will see a 🛠

Install the MyST Markdown CLI

🛠 Install MyST using your preferred packaging ecosystem. (See for more details).

:::{tip} Not sure which to pick?
:class: dropdown

The easiest way to install MyST is with the mamba package manager. mamba is a cross-platform language-agnostic package manager that is useful for users across many data science languages like Python, R, Julia, and JavaScript.
:::

:::{embed} #installing-myst-tabs
:::

🛠 Then, check that MyST has successfully been installed:

shell
$ myst -v
v1.3.4

Build your first MyST site

Next we'll download some sample content and use MyST to render it as a local web server!

Download example content

We provide an example project that includes a few simple Markdown files and some Jupyter Notebooks.
Through the course of the tutorials we will add content to these documents that show off some of the features of MyST.

🛠 Download the example content[^no-git], and navigate into the folder:

bash
git clone https://github.com/jupyter-book/mystmd-quickstart.git
cd mystmd-quickstart

[^no-git]: If you aren't familiar with git, it isn't required for this tutorial, you can download the zip file with the contents from the quickstart repository.

Initialize MyST in the content folder 🚀

Next we will create a myst.yml configuration file that is required to render your project.

🛠 Run myst

The myst command is a shortcut for myst init, which has a few more options for writing specific parts of the configuration file and a table of contents for your site.

shell
$ myst

Welcome to the MyST Markdown CLI!! 🎉 🚀

myst init walks you through creating a myst.yml file.

You can use myst to:

 - create interactive websites from Markdown and Jupyter Notebooks 📈
 - build & export professional PDFs and Word documents 📄

Learn more about this CLI and MyST Markdown at: https://mystmd.org

💾 Writing new project and site config file: myst.yml

Preview your MyST site locally

Preview a rendered version of the quickstart content to make sure that MyST is working properly.

🛠 When prompted, type Yes to install and serve your MyST content locally:

bash
? Would you like to run "myst start" now? Yes

or manually serve the quickstart content with the following command:

bash
myst start

Starting the server requires a theme, this will download the default book-theme from the MyST themes.
This can take up to a minute the first time, and then will be cached in the _build/templates directory.

text
🐕 Fetching template metadata from https://api.mystmd.org/templates/site/myst/book-theme
💾 Saved template to path _build/templates/site/myst/book-theme
⤵️ Installing web libraries (can take up to 60 s)
📦 Installed web libraries in 13 s
📖 Built interactive-graphs.ipynb in 21 ms.
📖 Built paper.md in 32 ms.
📖 Built README.md in 35 ms.
📚 Built 3 pages for myst in 82 ms.

  ✨✨✨  Starting Book Theme  ✨✨✨

⚡️ Compiled in 524ms.

🔌 Server started on port 3000!  🥳 🎉

  👉  http://localhost:3000  👈

🛠 Open your web browser to http://localhost:3000[^open-port]

[^open-port]: If port 3000 is in use on your machine, an open port will be used instead, follow the link provided in the terminal.

The example site in this tutorial only has three pages and by default the 01-paper.md page is seen in , which has minimal styles applied to the content.

:::{figure} ./images/frontmatter-before.png
:width: 50%
:label: frontmatter-before
:class: framed

The myst theme for the 01-paper.md page without any changes made.
:::

🎉 Congratulations, you just built your first MyST site!

Configuration and structure

The final section of this tutorial takes a closer look at the files that we just created, and aspects of your MyST project that can be customized.

Folder Structure

If you are using a text editor, for example VSCode, open up the folder to explore the files:

text
quickstart/
  ├── 🆕 _build
  │   ├── exports
  │   ├── site
  │   │   ├── content
  │   │   ├── public
  │   │   └── config.json
  │   ├── temp
  │   └── templates
  │       ├── site/myst/book-theme
  │       └── tex/myst/arxiv_two_column
  ├── images
  │   ├── image.png
  │   └── image.gif
  ├── 01-paper.md
  ├── 02-notebook.ipynb
  ├── README.md
  └── 🆕 myst.yml

Running myst init added:

  • myst.yml - the configuration file for your myst project and site
  • _build - the folder containing the processed content and other site assets, which are used by the local web server.

The _build folder also contains your templates (including the site template you installed) and any exports you make (when we build a PDF the exported document will show up in the _build/exports folder). You can clean up the built files at any time using myst clean[^clean-all].

[^clean-all]:
By default the myst clean command doesn't remove installed templates or cached web responses; however, the function can with a:
myst clean --all, or
myst clean --templates --cache.

text
Before deleting any folders `myst` will confirm what is going to happen, or you can bypass this confirmation with the `-y` option. For example:

```text
Deleting all the following paths:

  - _build/site
  - _build/templates

? Would you like to continue? Yes

🗑 Deleting: _build/site
🗑 Deleting: _build/templates
```

Configure site and page options

If we open and look inside our myst.yml we will see a basic configuration like this:

yaml
# See docs at: https://mystmd.org/guide/frontmatter
version: 1
project:
  # title:
  # description:
  keywords: []
  authors: []
  # github:
  # bibliography: []
site:
  template: book-theme
  # title:
  # options:
  #   logo: my_logo.png
  nav: []
  actions:
    - title: Learn More
      url: https://mystmd.org/guide
  domains: []

:::{note}
As this is a rapidly-evolving project, the actual generated myst.yml file that you will see may look a bit different.
:::

There are two important parts to the myst.yml:

project:
: The project holds metadata about the collection of files, such as authors, affiliations and licenses for all of the files, any of these values can optionally be overridden in a file. To see all of the options see , which includes which fields can be overridden by files in the project.

site:
: The site holds template information about the website, such as the logo, navigation, site actions and which template to use.

🛠 In myst.yml: Change the "# title:" comment in site to "title: Fancy Title 🎩" and save

Saving the myst.yml will have triggered a "full site rebuild"[^myst-start].
Take a look at the browser tab and you'll see that it has updated:

:::{figure} ./images/frontmatter-site-title.png
:width: 50%
:label: frontmatter-site-title

The site title will control site meta tags, and the browser-tab title, which is appended to each page title in the book-theme.
:::

[^myst-start]: If the server stopped, you can restart the server using myst start.

:::{seealso} See all Frontmatter options
To see all of the options see , which includes which fields can be overridden by files in the project.
:::


Next steps ➡️

That's it for this quickstart tutorial!
You've now got MyST installed locally, and the basic structure of a MyST project ready to improve.
You are well on your way to getting started with myst.

Here are some things to try for next steps:

(quickstart-cards)=
+++
Check out the following tutorials for more step-by-step guides:

::::{grid} 1 1 2 2
:::{card} Author enriched MyST articles ✨
:link: ./quickstart-myst-documents.md
Write scientific articles using MyST Markdown with easy-to-use citations, metadata, and cross-references.
:::

:::{card} Executable Documents with MyST 🐍
:link: ./quickstart-executable-documents.md
Learn how to use computation and execution with Jupyter in MyST.
:::

:::{card} Export to static documents 📑
:link: ./quickstart-static-exports.md
Export MyST documents to a Word document, PDF, and $\LaTeX$!
:::

:::{card} MyST Markdown Guide 📖
:link: ./quickstart-myst-markdown.md
See an overview of MyST Markdown syntax with inline demos and examples.
:::

:::{card} Use MyST in Jupyter Interfaces 🌕
:link: ./quickstart-jupyter-lab-myst.md
Learn how to use MyST in Jupyter Interfaces.
:::
::::


Syntax Overview (`docs/syntax-overview.md`)


title: MyST Syntax Overview

MyST (Markedly Structured Text) is designed to create publication-quality documents written entirely in Markdown. The extensions and design of MyST is inspired by the Sphinx and reStructuredText (RST) ecosystems.

MyST is a superset of CommonMark, the standard form of Markdown, and allows you to directly create “directives” and “roles” that extend Markdown to support technical and scientific documents. directives are block-level extension points, like callout panels, tabs, figures or embedded charts; and roles are inline extension points, for components like cross-references, external references, citations, or inline math.

Directives & Roles

Roles and directives are two of the most powerful parts of MyST. They are kind of like functions, but written in a markup language. They both serve a similar purpose, but roles are written in one line whereas directives span many lines. They both accept different kinds of inputs, and what they do with those inputs depends on the specific role or directive being used.

(syntax:directives)=

Directives

Directives are multi-line containers that include an identifier, arguments, options, and content. Examples include admonitions, figures, and equations (visit this page for the full list of directives). At its simplest, you can use directives using a "fence" (either back-ticks or colons[^colon-or-fence]) and the name of the directive enclosed in braces ({name}):

[^colon-or-fence]: Which fence type, colon or backtick, is up to you, and either will work. The colon-fence has better fallback when the contents of the directive includes Markdown in non-MyST renderers (like GitHub). The backtick-fence should be used when the contents of the directive is code-like (e.g. a diagram or math!).

(example-fence)=

{tab-set}
````{tab-item} Colon Fence
Use a colon fence (`:::`) when the contents of the directive is Markdown, such as [callouts](./admonitions.md) or  [theorems](./proofs-and-theorems.md) this will improve the processing in renderers that do not support MyST:

```{myst}

:::{note}
Here is a note!
:::
```
````
`````{tab-item} Backtick Fence
Use a backtick fence (`` ``` ``) when the contents of the directive is code-like, such as [math](./math.md) or a [diagrams](./diagrams.md), this will ensure any sort of auto-formatting will not attempt to reformat these sections:
````{myst}
```{math}
\mathbf{u} \times \mathbf{v}=\left|\begin{array}{ll}u_{2} & u_{3} \\ v_{2} & v_{3}\end{array}\right| \mathbf{i}+\left|\begin{array}{ll}u_{3} & u_{1} \\ v_{3} & v_{1}\end{array}\right| \mathbf{j}+\left|\begin{array}{ll}u_{1} & u_{2} \\ v_{1} & v_{2}\end{array}\right| \mathbf{k}
```
````
`````

The {myst:directive}note directive above doesn't take any arguments, and we didn't add any options. In addition to the directive name and the directive content, directives allow two other configuration points:

1) directive arguments - a list of words that come just after the {directivename}.

{tab-set}
````{tab-item} Colon Fence
```markdown
:::{directivename} arg1 arg2
My directive content.
:::
```
````
`````{tab-item} Backtick Fence
````markdown
```{directivename} arg1 arg2
My directive content.
```
````
`````

2) directive options - a collection of flags or key/value pairs that come just underneath {directivename}.

There are three ways to write directive options: as :key: value, as a YAML block, or directly inline.

{tab-set}
`````{tab-item} Key value pairs
Options can be included as `:key: val` pairs, which is the default way to include options.
````markdown
```{directivename}
:key1: metadata1
:key2: metadata2

My directive content.
```
````
`````
`````{tab-item} YAML
Options can also be included as `key: val` pairs enclosed by `---` lines, similar to document frontmatter.
This is parsed as YAML, and may be easier for listing many options.


````markdown
```{directivename}
---
key1: metadata1
key2: metadata2
---

My directive content.
```
````
`````
`````{tab-item} Inline Options
Options can be included inline. See [](./inline-options.md) for more information.
````markdown
```{directivename .class-name #label key="value"}
My directive content.
```
````
`````
{tip}
Specifying directive keywords with `:key:` or `---` will make no difference. Use the `:key: val` syntax as a shorthand for just one or two keywords. Use the `---` syntax if you have many keywords you wish to specify, or if some values will span multiple lines.

Roles

Roles are very similar to directives, but they are written entirely in one line. The syntax of a role is:

markdown
Some content {rolename}`and here is my role's content!`

Of course, roles will only work if rolename is a valid role name! The abbr role creates inline abbreviations, for example, {abbr}`MyST (Markedly Structured Text)` will become {abbr}MyST (Markedly Structured Text)! When you hover over[^1] the abbreviation you will see the title appear!

[^1]: Abbreviations are also great structured data for screen-readers!

Roles are defined inline, with an identifier and input. There are a number of roles included in MyST, including abbreviations, subscript, and superscript, as well as inline . Unknown roles will still be parsed as well:

{myst}
Here is an {abc}`unknown role`.

Options for roles can be included inline. See for more information.

markdown
Here is my {span #label .class-name key="value"}`custom span`.

(nesting-content)=

Nesting content blocks in Markdown

If you’d like to nest content blocks inside one another in Markdown (for example, to put a {note} inside of a {margin}), you may do so by adding extra backticks (`)[^colon] to the outer-most block.
For example, two admonitions nested in side of each other:

{myst}
````{important}
```{note}
Here's my `important`, highly nested note! 🪆
```
````

[^colon]: You can also use this same technique and add colons to the initial directive or fence. See an example of when to use each syntax.

This works for literal code blocks as well. For example, to show triple-backticks on this page we are using following syntax:

{myst}
````
```
```
````

Packages Jtex Docs Index (`packages/jtex/docs/index.md`)


title: jtex description: jtex is a command line tool and library for rendering LaTeX documents from Jinja-style templates.

jtex is a command line tool (CLI) for rendering $\LaTeX$ documents from Jinja-style templates. This package uses nunjucks (a port of Jinja) as the template engine with a modified environment and syntax that plays well with $\LaTeX$'s markup.

::::{grid} 1 1 2 3

:::{card}
:link: ./create-a-latex-template.md

Create a Template 🆕
^^^
Learn how to create a $\LaTeX$ template for a scientific journal article or Beamer presentation using jtex and MyST Markdown.
:::

:::{card}
:link: https://github.com/myst-templates

Browse Templates 🚀
^^^
Our $\LaTeX$ templates are curated by the MyST community and include templates to render over 400 journals!
:::

:::{card}
:link: ./command-line.md

Command Line Interface ✨
^^^
Use jtex to list templates, quickly render content, or fix your template.yml, or parse your $\LaTeX$ files for used packages.
:::
::::

{important}
:class: dropdown
# Looking for Templates?

MyST Templates are available in the [myst-templates organization](https://github.com/myst-templates) on GitHub. You can also see all listed community templates using `myst templates list`, or browse the [api](https://api.mystmd.org/templates/tex) if you are into JSON.

To create your own template see [](./create-a-latex-template.md).

Project Goals

  • Provide a data-driven templating markup for $\LaTeX$
  • Provide validation of templates and supplied options when rendering a template
  • Work with standardized frontmatter to make authors, affiliations, etc. easy to template
  • Work with the MyST ecosystem of tools
  • Support many user supplied templates, local templates, etc.

Not Goals

Installation

Install the package globally using npm:

bash
npm install jtex

and confirm correct installation by typing:

bash
jtex --version

Usage with MyST

The main usage for jtex is with myst and curvenote command line tools,
both of which can compile markdown into $\LaTeX$ for templating with jtex.

Simple Example

A document can be used very simply with the document as doc, the contents of which are
documented in . Be sure to include the [-IMPORTS-] and the [-CONTENT-].

latex
% template.tex
\documentclass{article}

[-IMPORTS-]

\title{[-doc.title-]}
\author{[-doc.authors[0].name-] ([-doc.authors[0].email-])}

\begin{document}
\maketitle

[-CONTENT-]

The End!
\end{document}

Packages Myst Cli Docs Index (`packages/myst-cli/docs/index.md`)


title: myst-cli description: myst-cli is a command line tool for processing MyST markdown files

myst-cli is a command line tool for processing MyST markdown files


Packages Myst Parser Docs Index (`packages/myst-parser/docs/index.md`)

myst-parser




{abbr}MyST (Markedly Structured Text) is a flavor of markdown inspired from the Sphinx ecosystem and {abbr}RST (reStructuredText).

Any CommonMark markdown (such as Jupyter Notebook markdown) is natively supported by the MyST parser. The goal of MyST javascript parser (myst-parser) is to have overlap with Sphinx ecosystem and {abbr}RST (reStructuredText) and provide a flexible parser and renderer that can be used with other content workflows.

{important}
For integration with **Sphinx**, use the Python reference implementation for MyST parser, which can be found at:

<https://myst-parser.readthedocs.io/en/latest/>

The following documentation refers only to the Javascript MyST parser.

The focus of mystmd is on components that are useful in rendering books, papers & reports. This project is associated with JupyterBook and maintained by the Executable Books Community.

Package Responsibilities

The myst-parser package provides a Javascript implementation of the MyST parser, with the many of the standard directives and roles included.

  • Parse MyST flavored markdown, which includes directives, roles, and block elements
  • Export a Markdown Abstract Syntax Tree (mdast) that is tested against the MyST specification
  • Provide extension point for additional directives and roles to be added
  • Provide a serializer into standard html for all known roles and directives
    • CSS styling will not be enforced or provided by this repository
  • Provide extension point for cross-referencing between multiple documents

Supported Syntax

  • CommonMark Markdown
  • Admonitions (callouts)
  • References and citations
  • Figures, with cross-references
  • Math & equations, with cross-references
  • Common roles and directives that constitute "Core MyST"

The myst-parser package currently uses markdown-it for parsing markdown. This includes packages that are elsewhere in the executablebooks community including:

The package also includes community supported markdown-it plugins (e.g. markdown-it-footnote, and others as necessary).

{note}
`myst-parser` may adopt portions of the [unified](https://unifiedjs.com/) ecosystem in the future, but will continue to support basic `markdown-it` parsing as this is required in extending markdown parsers in, for example, [VSCode](https://github.com/executablebooks/myst-vs-code).

Packages Myst Spec Docs Index (`packages/myst-spec/docs/index.md`)

MyST Specification

MyST (Markedly Structured Text) is designed to create publication-quality, computational documents written entirely in Markdown. The main use case driving the development and design of MyST is JupyterBook, which creates educational online textbooks and tutorials with Jupyter Notebooks and narrative content written in MyST.

Overview

There are three components that this documentation and repository aims to formalize:

  1. the MyST markup language, which is a superset of CommonMark (a standard form of Markdown);
  2. the MyST abstract syntax tree (AST), which is the datastructure of a document after it has been parsed, and can be used in transforming and rendering content; and
  3. a set of unit-tests that can be used to test implementations of MyST (e.g. in Python and Javascript).

MyST Markup Language

MyST is a superset of CommonMark (a standard form of Markdown) and allows you to directly create “directives” and “roles” as extension points in the language. These extension points are influenced by ReStructured Text (RST) and Sphinx -- pulling on the nomenclature and introducing additional standards where appropriate. directives are block-level extension points, like callout panels, tabs, figures or embedded charts; and roles are inline extension points, for components like references, citations, or inline math.

MyST Abstract Syntax Tree

Fundamentally, the specification for MyST documents can be broken down into abstract syntax tree (AST) node types and their document-level counterparts. The myst-spec AST builds on Markdown AST, or mdast, an intermediate format that in turn builds upon the existing Universal Syntax Tree, or unist spec. unist is used throughout the unifiedjs Javascript community with hundreds of existing transformations, utilities and serializers. unist has a simple format, with a type defining the node optional properties on the node and optional children (a leaf node has a value).

The MyST AST introduces nodes for directives and roles, as well as numerous new features, like admonitions, citiations, and equations, all of which build upon mdast and other existing standards and nomenclature. The MyST AST, like mdast, is serializable to JSON or YAML, and can be effectively shared between projects, languages, and implementations.

MyST Test Cases

To validate implementations of the MyST spec, a suite of test cases is provided here in myst.tests.json. This file is a JSON list of test cases with MyST markup, MyST AST, and example HTML in some cases. The cases are similar to CommonMark test cases with the addition of the AST representation. The MyST test cases include all the CommonMark examples as well as many MyST specific examples.

{note}
These test cases are still being expanded as the spec is developed.  They do not yet include all edge cases and are also subject to change as the spec is developed.
{attention}
**Test Cases**

Test cases are available for download here: https://unpkg.com/browse/myst-spec/dist/

Working with the MyST AST

{warning}
The `myst-spec` AST is in development; any structures or features present in the JSON schema may change at any time without notice.

This documentation contains all the node types used to construct a MyST document. Conventionally, the AST representation of a MyST document represents the state immediately after parsing. This means, for example, references are not resolved, directive/role structures are still present, etc. You may work with AST nodes by either modifying or adding to them to modify and update the corresponding MyST document, or you may transform and reduce them into a simpler structure to consume in other contexts.


Packages Myst To Jats Docs Index (`packages/myst-to-jats/docs/index.md`)


title: myst-to-jats description: myst-to-jats is library for converting MyST documents to JATS XML.

myst-to-jats is a library for converting MyST documents into JATS.

Goals

  • Convert from myst-spec AST documents into JATS XML

Not Goals

  • Read JATS, this package is for serialization only - see jats-to-myst

Installation

Install the package into your virtual environment using npm:

bash
npm install myst-to-jats

Simple example

Below we construct a AST tree using unist-builder, use the mystToJats
plugin in a unified pipeline and stringify the document into a $\LaTeX$ file.

typescript
import { unified } from 'unified';
import { u } from 'unist-builder';
import mystToJats from 'myst-to-jats';

// Create a AST document, or parse using mystmd
const tree = u(
  'root',
  u('paragraph', [
    u('text', { value: 'This is a unicode “fraction”: ' }),
    u('inlineMath', { value: '½' }),
  ]),
);
// Use the plugin, and stringify the tree
const file = unified().use(mystToJats).stringify(tree);
// Log the results
console.log(file.result.value);

The document that we fed in was quite simple, and the ½ unicode characters as well as the quotes are nicely transformed into the appropriate $\LaTeX$:

latex
This is a unicode ``fraction'': $\frac{1}{2}$

Overview

The myst-to-jats library aims to translate all standard MyST syntax to sensible $\LaTeX$ syntax, as well as be extensible to other plugins.


Packages Myst To Md Docs Index (`packages/myst-to-md/docs/index.md`)


title: myst-to-md description: myst-to-md is library for converting MyST abstract syntax trees to markdown.

myst-to-md is a library for converting MyST abstract syntax trees to markdown.

Goals

  • Convert from myst-spec AST documents into markdown
  • Work as a unifiedjs plugin and in the mystmd ecosystem

Not Goals

  • Read Markdown, this package is for serialization only

Installation

Install the package into your virtual environment using npm:

bash
npm install myst-to-md

Simple example

Below we construct a AST tree using unist-builder, use the mystToMd
plugin in a unified pipeline and stringify the document into a Markdown file.

typescript
import { unified } from 'unified';
import { u } from 'unist-builder';
import mystToMd from 'myst-to-md';

// Create a AST document, or parse using mystmd
const tree = u(
  'root',
  u('paragraph', [
    u('text', { value: 'This is a unicode “fraction”: ' }),
    u('inlineMath', { value: '½' }),
  ]),
);
// Use the plugin, and stringify the tree
const file = unified().use(mystToMd).stringify(tree);
// Log the results
console.log(file.result.value);

Overview

The myst-to-md library aims to translate all standard MyST syntax to markdown, as well as be extensible to other plugins.


Packages Myst To Tex Docs Index (`packages/myst-to-tex/docs/index.md`)


title: myst-to-tex description: myst-to-tex is library for converting MyST documents to LaTeX.

myst-to-tex is a library for converting MyST documents into LaTeX.

Goals

  • Convert from myst-spec AST documents into $\LaTeX$ markup
  • Work with jtex to template documents
  • Work as a unifiedjs plugin and in the mystmd ecosystem

Not Goals

  • Read LaTeX, this package is for serialization only

Installation

Install the package into your virtual environment using npm:

bash
npm install myst-to-tex

Simple example

Below we construct a AST tree using unist-builder, use the mystToTex
plugin in a unified pipeline and stringify the document into a $\LaTeX$ file.

typescript
import { unified } from 'unified';
import { u } from 'unist-builder';
import mystToTex from 'myst-to-tex';

// Create a AST document, or parse using mystmd
const tree = u(
  'root',
  u('paragraph', [
    u('text', { value: 'This is a unicode “fraction”: ' }),
    u('inlineMath', { value: '½' }),
  ]),
);
// Use the plugin, and stringify the tree
const file = unified().use(mystToTex).stringify(tree);
// Log the results
console.log(file.result.value);

The document that we fed in was quite simple, and the ½ unicode characters as well as the quotes are nicely transformed into the appropriate $\LaTeX$:

latex
This is a unicode ``fraction'': $\frac{1}{2}$

Overview

The myst-to-tex library aims to translate all standard MyST syntax to sensible $\LaTeX$ syntax, as well as be extensible to other plugins.


Packages Myst Transforms Docs Index (`packages/myst-transforms/docs/index.md`)


title: myst-transforms description: myst-transforms contains a number of transformations for MyST ASTs

myst-transforms is a library for transforming myst markdown documents.

Overview

These transforms take AST from mystmd and turn it into usable forms, enforce document structure, or provide other utilities.

These utilities are available on npm:

bash
npm install myst-transforms

You can use the plugins as follows:

typescript
import { unified } from 'unified';
import { mathPlugin } from 'myst-transforms';

unified()
  .use(mathPlugin, { macros: {} }) // Add the plugin with any options
  .run(tree); // Run the AST through the set of plugins

// The AST has now been modified in place.

Packages Myst Transforms Docs Overview (`packages/myst-transforms/docs/overview.md`)


title: What's in store description: List of transforms included in the myst-transforms package.

List of Transforms

Admonitions
: Add headers to each admonition (callout), such as Note or Important.
: Optionally replace the header with the first paragraph of text if it is all bold.

Blocks
: Ensure that all blocks in a document are not nested.

Blockquotes
: Change attributed blockquotes, those that include a single list item to a figure of kind quote, and add the attribution as a figure caption.

Caption
: Ensure that figure captions are in paragraphs

Code
: Audit code to ensure that languages are specified, or provide a default language
: Ensure that python as a language is replaced from IPython3 or other kernal-like language specifiers.

CodeBlock
: Codeblocks can live inside of figures, have captions and be references. Transformation ensures that this information isn't lost from the original parsing of AST.
: Note: this should move into the parser in the future.

Footnotes
: Pull the footnote definitions into a reference object.

HTML
: Parse HTML nodes and replace their contents.

HTML Ids
: Ensure that referenceable nodes have proper HTML Ids (start with a letter, no spaces or fancy characters)
: HTML Ids are more strict than the internal labels used for cross-references, and this is up to the transform to provide, not the renderer.

Images
: Add alt text to images if it is not defined, from the captions.

Keys
: Add unique identifiers to each node, helpful for rendering as well as referring to a document in the wild. These are not aiming to be persisted across multiple builds of the document, for that you should use a label (and associated HTML Id).

Math
: Use KaTeX to create and provide errors/warnings about math rendering.

Math Label
: Parse latex to strip \label and simple equation environments from math, and put this in AST directly.

MyST Cleanup
: A few utilities for lifting children and getting rid of some information about the parsed document from MyST. For example, the mystDirective and mystRole wrapper nodes are removed.

Targets
: Propagate targets (i.e. (my-id)=) to the following node
: Add HTML friendly identifiers to headings.

Plugin Collections

A few of the plugins are exposed in a basicTransformationsPlugin that does a number of the expected transformations without any user supplied options.
See the code (look for function basicTransformations in file packages/myst-transforms/src/basic.ts) for exactly which plugins are included.

If you are depending on this, ideally use the individual plugins directly, which will always provide options.


Packages Myst Spec Docs Features Overview (`packages/myst-spec/docs/features/overview.md`)

Overview

MyST (Markedly Structured Text) is designed to create publication-quality documents written entirely in Markdown. The main use case driving the development and design of MyST is JupyterBook, which helps you create educational online textbooks and tutorials with Jupyter Notebooks and narrative content written in MyST. The extensions and design of MyST is inspired by the Sphinx and ReStructured Text (RST) ecosystems.

MyST is designed to harness the extensibility and community of RST and bring these super-powers into Markdown.

MyST is a superset of CommonMark (the standard form of Markdown) and allows you to directly create “directives” and “roles” as extension points in the language. directives are block-level extension points, like callout panels, tabs, figures or embedded charts; and roles are inline extension points, for components like references or inline math.

Directives & Roles

Roles and directives are two of the most powerful parts of MyST. They are kind of like functions, but written in a markup language. They both serve a similar purpose, but roles are written in one line whereas directives span many lines. They both accept different kinds of inputs, and what they do with those inputs depends on the specific role or directive being used.

Directives

Directives are multi-line containers that include an identifier, arguments, options, and content. Examples include admonitions, figures, and equations.

Specification

{include} ../nodes/directive.md

Example

{include} ../examples/directive.md

In addition to the directive name and the directive content, directives allow two other configuration points:

1) directive arguments - a list of words that come just after the {directivename}.

text
```{directivename} arg1 arg2
My directive content.
```

2) directive options - a collection of flags or key/value pairs that come just underneath {directivename}.

There are two ways to write directive options

{list-table}
* - 2a) Options as `:key: val` pairs.\
    Great for a few options.
  - 2b) Options as `key: val` pairs enclosed by `---` lines.\
    This is parsed as YAML, and easier for listing many options.
* - ````
    ```{directivename}
    :key1: metadata1
    :key2: metadata2

    My directive content.
    ```
    ````
  - ````
    ```{directivename}
    ---
    key1: metadata1
    key2: metadata2
    ---

    My directive content.
    ```
    ````
{tip}
Remember, specifying directive keywords with `:key:` or `---` will make no difference. We recommend using `---` if you have many keywords you wish to specify, or if some values will span multiple lines. Use the `:key: val` syntax as a shorthand for just one or two keywords.

Roles

Roles are very similar to directives, but they are written entirely in one line.

Specification

{include} ../nodes/role.md

Example

{include} ../examples/role.md

Of course, roles will only allow custom behavior work if name is a valid role name! The abbr role creates inline abbreviations, for example, {abbr}`MyST (Markedly Structured Text)` will become {abbr}MyST (Markedly Structured Text)! When you hover over[^1] the abbreviation you will see the title appear!

[^1]: Abbreviations are also great structured data for screen-readers!

Roles are defined inline, with an identifier and input. There are a number of roles included in MyST, including abbreviations, subscript, and superscript, as well as inline .

Nesting content blocks in Markdown

If you’d like to nest content blocks inside one another in Markdown (for example, to put a {note} inside of a {margin}), you may do so by adding extra backticks (`) to the outer-most block. This works for literal code blocks as well.

For example, the following syntax:

text
````
```
```
````

yields

text
```
```

Thus, if you’d like to nest directives inside one another, you can take the same approach. For example, the following syntax:

text
````{important}
```{note}
Here's my `important`, highly nested note! 🪆
```
````

produces a nested, important > note:

{important}
```{note}
Here's my `important`, highly nested note! 🪆
```

Abbreviations.Yml (`docs/abbreviations.yml`)

version: 1
project:

abbreviations:
# Alphabetical order
AMS: American Mathematical Society
API: Application Programming Interface
AST: Abstract Syntax Tree
CLI: Command Line Interface
CRediT: Contributor Roles Taxonomy
CSS: Cascading Style Sheet
CSV: comma-separated values
DOI: Digital Object Identifier
ESM: ECMAScript Modules
GPU: Graphics Processing Unit
HTML: Hypertext Markup Language
ID: Identifier
ISNI: International Standard Name Identifier
JATS: Journal Article Tag Suite
JSON: JavaScript Object Notation
LTS: Long Term Support
MyST: Markedly Structured Text
NISO: National Information Standards Organization
OA: Open Access
PDF: Portable Document Format
PID: Persistent Identifier
POSIX: Portable Operating System Interface
REES: Reproducible Execution Environment Specification
ROR: Research Organization Registry
RRID: Research Resource Identifier
RST: reStructuredText
SPDX: Software Package Data Exchange
TLA: Three Letter Acronym
TOC: table of contents
TPU: Tensor Processing Unit
URL: Uniform Resource Locator
VoR: Version of Record
WASM: WebAssembly
YAML: Yet Another Markup Language
XML: Extensible Markup Language


Accessibility And Performance (`docs/accessibility-and-performance.md`)


title: Accessibility and Performance short_title: Accessibility & Performance description: MyST builds documents and websites that are accessible to everyone and quick to load on any device.

Accessibility

Most of the user-facing interface lives in myst-theme.
These default themes build on top of Remix, which provides sensible accessibility defaults. This section describes the broader goals and considerations around accessibility for the MyST themes.[^scope]

[^scope]: This page is scoped to the default MyST themes

text
This page describes the **default MyST website themes** (the Remix-based [`book` and `article` themes](https://github.com/jupyter-book/myst-theme) that ship with `mystmd`). Sites built with a custom theme or a different renderer may behave differently.

For more information about accessibility across all of Jupyter, see the [Jupyter Accessibility Working Group](https://jupyter-accessibility.readthedocs.io).

:::{card} Open an a11y issue »
:link: https://github.com/jupyter-book/myst-theme/issues/new?labels=a11y
If you find a barrier on a MyST site, open an issue in myst-theme.
A clear way to reproduce helps even if you can't propose a fix.
:::

Our accessibility goals

The default MyST web themes aim to meet WCAG 2.1 AA, the level required of US public-sector and many private websites under ADA Title II.

Accessibility bugs there are treated like any other bug.
See the a11y issues label for open accessibility issues.
Accessibility contributions follow the normal contribution process.

How accessible is MyST today?

The MyST themes ship with these accessibility behaviors:

  • Semantic HTML for articles, asides, figures, navigation, and captions.
  • A skip-to-content shortcut and full keyboard navigation for menus, search, and the table of contents.
  • Focusable, scrollable cell outputs so keyboard and screen-reader users can reach long stdout, stderr, and equation output.
  • Color-contrast-aware defaults in the book and article themes, including code cells and error states.
  • Pre-rendered math that ships as accessible HTML rather than images.
  • Alt-text for images sourced from figure captions when present.
  • aria labels and roles on interactive controls (theme toggle, document outline, search dialog, footnote return links).

In April 2026 we completed a focused round of work to align with WCAG 2.1 AA. We know there is always ongoing work to be done to improve accessibility and welcome both guidance and contributions, see .

(a11y:monitor)=

How we monitor accessibility

On every push to main, myst-theme runs an automated accessibility audit in CI.
It builds the current themes and runs berkeley-cdss/myst-a11y (which wraps axe-core) against the reference pages, checking WCAG 2.0 and 2.1 (A & AA).

Results are surfaced in a tracking issue with the a11y-audit label.
It summarizes the current issue counts broken down by rule (such as color-contrast, label, and image-alt) and by page.
If an issue with that label is already open, it will be updated on each new workflow run.
If there is no open issue, then a new one is created.

Cell outputs are not under MyST's control

Many libraries emit interactive JavaScript or images as part of executing code (e.g., Plotly, ipywidgets, etc).
In these cases, MyST does not have control over the structure of what is created, and cannot guarantee their accessibility.
If you find a cell output that is not accessible, please report it in the repository for the tool that produced it.
The fix will most likely need to happen in that upstream tool rather than in the MyST theme.

(a11y:contribute)=

Where can I follow along or contribute?

Open accessibility work is tracked under the a11y label in myst-theme.
There's a thread for broader discussion and tracking in the Accessibility Improvements tracking issue.

(a11y:check-your-site)=

How can I check the accessibility of my own site?

A few tools that others in the community have found useful:

  • The Jupyter Accessibility Working Group has a collection of accessibility resources for the broader Jupyter community.
  • berkeley-cdss/myst-a11y is a GitHub Action built for MyST sites. It runs axe-core checks against WCAG 2.0 and 2.1 (A and AA) on every push and reports results as a tracking issue. This is the same action MyST runs on its own themes, see . For a working example, see data-8/textbook.
  • Lighthouse is built into Chrome DevTools and is useful for a quick audit of a single page.
  • JupyCheck checks accessibility of source notebooks in Jupyter interfaces. This is useful if you also want readers to launch Jupyter sessions from your MyST site.

Performance

The default MyST themes are built on Remix and React. These provide link prefetching, smaller download sizes through modern bundlers, image optimization, and faster page transitions that update only the part of the page that changed.
We follow the PRPL pattern where possible:

  • Preload the most important resources.
  • Render the initial route as soon as possible.
  • Pre-cache remaining assets.
  • Lazy load other routes and non-critical assets.

Hover over a link in the navigation with your browser's developer tools open: the next page is fetched as soon as you hover, before you click. Only that page's content and metadata are downloaded, not a full HTML document, and shared assets like fonts and styles are reused across pages. Pages you've already visited are served from your browser's cache.

Locally, MyST rebuilds and rerenders in under 150ms and restores your scroll position, so you can preview changes without losing your place.

Performance of a deployed site depends on the infrastructure that serves it. See deployment for options.

Lighthouse

Lighthouse is a Chrome tool that measures performance, accessibility, and search-engine readiness. It flags asset, image, and crawler issues that affect both real-world load times and search rankings.

{figure} ./images/lighthouse-2022_09_15.png
:label: lighthouse
A 2022 Lighthouse run on a deployed MyST site using Curvenote's global CDN.
{warning}
Lighthouse scores depend on your network, machine, and the specific page measured; the figure above is a 2022 snapshot. If you find a place where MyST performance can be improved, please [open an issue](https://github.com/jupyter-book/mystmd/issues).

Admonitions (`docs/admonitions.md`)


title: Callouts & Admonitions description: Callout blocks or admonitions, like "notes" or "hints" are outlined or shaded areas of a document to bring attention to particular information. thumbnail: ./thumbnails/admonitions.png

Callouts, or "admonitions", highlight a particular block of text that exists slightly apart from the narrative of your page, such as a note or a warning.

In MyST we call these kinds of directives {myst:directive}admonitions <admonition>, however, they are almost always used through their named directives, like {myst:directive}note or {myst:directive}danger. There are ten kinds[^docutils-admonitions] of admonitions available by default, and others may be introduced by plugins:

{list-table} named admonitions that can be used as directives
:label: admonitions-list
* - 🔵 {myst:directive}`note`
  - 🟠 {myst:directive}`attention`
* - 🔵 {myst:directive}`important`
  - 🟠 {myst:directive}`caution`
* - 🟢 {myst:directive}`hint`
  - 🟠 {myst:directive}`warning`
* - 🟢 {myst:directive}`seealso`
  - 🔴 {myst:directive}`danger`
* - 🟢 {myst:directive}`tip`
  - 🔴 {myst:directive}`error`

[^docutils-admonitions]: These admonitions are the same as those used in docutils and Sphinx.

For example, try changing the following example of a {tip} admonition to a {warning}:

{myst}
:::{tip}
Try changing `tip` to `warning`!
:::

See below for a demo of each admonition in the default theme.

{tab-set}
````{tab-item} Note
```{note}
This is an note admonition
```
````

````{tab-item} Important
```{important}
This is an important admonition
```
````

````{tab-item} Hint
```{hint}
This is an hint admonition
```
````

````{tab-item} See Also
```{seealso}
This is an seealso admonition
```
````

````{tab-item} Tip
```{tip}
This is an tip admonition
```
````

````{tab-item} Attention
```{attention}
This is an attention admonition
```
````

````{tab-item} Caution
```{caution}
This is an caution admonition
```
````

````{tab-item} Warning
```{warning}
This is an warning admonition
```
````

````{tab-item} Danger
```{danger}
This is an danger admonition
```
````

````{tab-item} Error
```{error}
This is an error admonition
```
````

Admonition Titles

All admonitions have a single argument ({myst:directive}docs <admonition.arg>), which is the admonition title and can use Markdown.
If a title argument is not supplied the first node of the {myst:directive}admonition.body is used if it is a heading or a paragraph with fully bold text; otherwise the name of the directive is used (e.g. seealso becomes See Also; note becomes Note).

{myst}
:::{tip} Admonition _title_
Here is an admonition!
:::

(admonition-github-compatibility)=
:::::::{tip} Compatibility with GitHub
:class: dropdown
GitHub Markdown transforms blockquotes that start with a bold Note or text with [!NOTE] into a simple admonition (see GitHub). This syntax only works for note, important or warning. MyST transforms these blockquotes into the appropriate admonitions with a simple {myst:directive}admonition.class.

{myst}
> [!NOTE]
> Highlights information that users should take into account, even when skimming.

> [!IMPORTANT]
> Crucial information necessary for users to succeed.

> [!WARNING]
> Critical content demanding immediate user attention due to potential risks.

> **Note**
> This is a note

> **Warning**
> This is a warning

:::::::

::::{tip} Compatibility with Pandoc & Quarto
:class: dropdown
In Quarto/Pandoc Markdown admonitions are styled with special classes (like {.callout-note} or {.callout-tip}).
If you are using Jupyter Book V1 or Sphinx documentation, use an {myst:directive}admonition directive with the specific class, for example:

{myst}
::: {.callout-tip}
## Tip with Caption
This is an example of a callout with a caption.
:::

::::

::::{warning} Compatibility with Sphinx
:class: dropdown
In Sphinx, all named admonitions (like {note} or {tip}), have no arguments.
If you place content on the first line it will instead be prepended to the admonition body.
If you are using Jupyter Book V1 or Sphinx documentation, use an {myst:directive}admonition directive with the specific class, for example:

{myst}
:::{admonition} The Title
:class: hint
This is the body.
:::

::::

(admonition-dropdown)=

Admonition Dropdown

To turn an admonition into a dropdown, add the dropdown {myst:directive}admonition.class to them.
Dropdown admonitions use the <details> HTML element (meaning they also will work without JavaScript!),
and they can be helpful when including text that shouldn't immediately visible to your readers.
To have a dropdown-style admonition start open, add the {myst:directive}admonition.open option.

{myst}
:::{note} Click Me! 👈
:class: dropdown
👋 This could be a solution to a problem or contain other detailed explanations.
:::

:::{note} Hide Me! 👈
:class: dropdown
:open: true
👋 This is an admonition with a lot of text. The user can hide it if they want.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
:::

:::{seealso} You can also use a {dropdown}
:class: dropdown
You can also use a {myst:directive}dropdown directive, which provides a more compact writing experience and is simpler in the displayed style. See for more information.
:::

Simpler Admonitions

Admonitions can additionally be styled as simple, and can optionally hide the icon using the {myst:directive}admonition.icon option.

{myst}
:::{important} Magic
:class: simple

This is a magic cat. It casts a luck spell on you that lasts an hour. \
**つ( ・ω・。)つ━☆・*。**
:::

Removing the icon from an admonition allows using a custom emoji for style.

{myst}
:::{danger} 🎤 Transcript. **Speaker:** John Smith
:icon: false
— To begin this lecture, I would like to ask the audience some questions.

...

— The next assignment has to be handed in by 01.05. Thanks everyone for attending.
:::

Multiple classes can be combined using, for example, inline options:

{myst}
:::{warning .simple .dropdown icon=false open=true} ✍️ NB
The proof of the lemma for $x \leqslant 0$ is left to the reader.
:::

Analytics (`docs/analytics.md`)


title: Adding Analytics to your MyST Website short_title: Analytics description: Add Google Analytics or Plausible to your MyST website with a site configuration key. thumbnail: ./thumbnails/analytics.png

By default, MyST sites contain no tracking cookies or analytics.
However, we offer support for two analytics tracking systems:

  1. Google Analytics; and
  2. Plausible, which is privacy-friendly alternative to Google.

These are set using template specific options in your site configuration using either an analytics_google or an analytics_plausible key.
See for more on site options and how to set them.

Google Analytics

Use the site.options.analytics_google configuration, with the contents being your Measurement ID (G-XXXXXX) or Tracking ID (UA-XXXXXX).

yaml
version: 1
site:
  options:
    analytics_google: UA-XXXXX # Measurement ID or Tracking ID

See Google Analytics docs for more information on how to find this Measurement ID.

Plausible

Use the site.options.analytics_plausible configuration, with the contents being the domain you are tracking.

yaml
version: 1
site:
  options:
    analytics_plausible: mystmd.org # Domain(s) to track

See Plausible docs for more information on how to find the domain. Note, you only copy in the contents of: data-domain="COPY_THIS", which can be a comma-separated list for multiple domains.

Testing your integration

Run myst start and view the page source. The analytics section should be in the <head> section of the HTML, you should also start to see real-time tracking in your dashboard.


Asides (`docs/asides.md`)


title: Asides, Margin Content, and Sidebars short_title: Asides & Margins description: Asides provide an easy way to represent content that is only indirectly related to the article's main content, such as in the sidebar or margin.

Asides provide an easy way to represent content that is only indirectly related to the article's main content.
Where supported, MyST will attempt to display an {myst:directive}aside as close as possible to—but separate from—the main article, such as in the side-margin.

{aside} margin content
Here’s some margin content! It was created by using the {myst:directive}`margin` directive in a Markdown, we can include images:



or any other sort of content!
markdown
:::{aside} Margin Content
Here’s some margin content! It was created by using the {myst:directive}`margin` directive in a Markdown, we can include images:



or any other sort of content!
:::

You can also refer to this directive using {myst:directive}margin or {myst:directive}sidebar, which will have slightly different classes applied in the future.

:::{warning} Experimental

Many of the features on this page are experimental and may change at any time.

These elements can conflict with the document outline when they are both competing for the margin space (see #170).

There is, as yet, no plugin available for transforming this directive into Typst.
:::

% Using bold here, until we fix #170

Topics

A topic is like a block quote with a title, or a self-contained section.
Use the "topic" directive to indicate a self-contained idea that is separate from the flow of the document.
Semantically, this is an <aside>.

markdown
:::{topic} This is an optional topic title
This is a topic, with standalone ideas.
:::

:::{topic} This is an optional topic title
This is a topic, with standalone ideas.
:::


Authorship (`docs/authorship.md`)

Authorship and attribution

A MyST project can include metadata about the authors of each page, such as institutional affiliations, funding sources, etc.

How to set author information

Author information is set in the MyST frontmatter.
You can set this either in myst.yml (in which case, they will apply to all documents in a MyST project), or in the page frontmatter (in which case they'll just apply to that page).

For example, the examples below show frontmatter for authors and author affiliations in frontmatter of an article.md file, and in myst.yml:

::::{tab-set}
:::{tab-item} article.md

yaml
---
title: My Article
authors:
  - name: Marissa Myst
    affiliation: ubc
  - name: Miles Mysterson
    affiliations: ubc; stanford
---

My article text begins here...

:::
:::{tab-item} myst.yml

yaml
affiliations:
  - id: ubc
    institution: University of British Columbia
    ror: https://ror.org/03rmrcq20
    isni: 0000 0001 2288 9830
    department: Department of Earth, Ocean and Atmospheric Sciences
    address: 2020 – 2207 Main Mall
    city: Vancouver
    region: British Columbia
    country: Canada
    postal_code: V6T 1Z4
    phone: 604 822 2449
  - id: stanford
    name: ...

:::
::::

See the authors frontmatter reference for a list of all authorship metadata fields you can use.

Re-use author information across repositories and projects

By extending MyST configuration files you can store author information in one place and re-use it across many other locations.
This allows you to define a source of truth for authorship and affiliations, and reference it over time. For example:

In one file, you'd define all the author affiliations that are relevant to your project:

{code-block} yaml
:filename: authors.yml
version: 1
project:
  contributors:
    - id: person_a
      name: First Last
      email: person_a@org_a.org
      orcid: XXXX-XXXX-XXXX-XXXX
      github: person_a
      affiliations:
        - id: org_a
        - id: org_b
    - id: person_b
      name: First Last
      email: person_b@org_b.com
      orcid: XXXX-XXXX-XXXX-XXXX
      github: person_b
      affiliations:
        - id: org_b
  affiliations:
    - id: org_a
      name: Organization A Incorporated
    - id: org_b
      name: Org B Inc.

And in each of your project myst.yml files, you can extend this authorship file like so to include all the information:

{code-block} yaml
:filename: myst.yml
version: 1
extends:
  - ./authors.yml  # Or, the URL of this file if not hosted locally
project:
  authors:
    - person_a
    - person_b

You could then also reference these authors in your page frontmatter like so:

{code-block} markdown
:filename: mypage.md
---
authors:
- person_a
---

Page content...

(affiliations)=

Affiliations

You can create an affiliation directly by adding it to an author.
It can be as simple as a single string.

yaml
authors:
  - name: Marissa Myst
    affiliation: University of British Columbia

You can also add much more information to any affiliation, such as a ROR, ISNI, or an address.
A very complete affiliations list for an author at the University of British Columbia is:

yaml
authors:
  - name: Marissa Myst
    affiliations:
      - id: ubc
        institution: University of British Columbia
        ror: https://ror.org/03rmrcq20
        isni: 0000 0001 2288 9830
        department: Department of Earth, Ocean and Atmospheric Sciences
        address: 2020 – 2207 Main Mall
        city: Vancouver
        region: British Columbia
        country: Canada
        postal_code: V6T 1Z4
        phone: 604 822 2449
        bluesky: '@eoas.ubc.ca'
  - name: Miles Mysterson
    affiliation: ubc

Notice how you can use an id to avoid writing this out for every coauthor.
Additionally, if the affiliation is a single string and contains a semi-colon ; it will be treated as a list.

If you use a string that is not recognized as an already defined affiliation in the project or article frontmatter, an affiliation will be created automatically and normalized so that it can be referenced:

::::{tab-set}
:::{tab-item} Written Frontmatter

yaml
authors:
  - name: Marissa Myst
    affiliations:
      - id: ubc
        institution: University of British Columbia
        ror: 03rmrcq20
        department: Earth, Ocean and Atmospheric Sciences
      - ACME Inc
  - name: Miles Mysterson
    affiliation: ubc

:::
:::{tab-item} Normalized

yaml
authors:
  - name: Marissa Myst
    affiliations: ['ubc', 'ACME Inc']
  - name: Miles Mysterson
    affiliations: ['ubc']
affiliations:
  - id: ubc
    institution: University of British Columbia
    ror: https://ror.org/03rmrcq20
    department: Earth, Ocean and Atmospheric Sciences
  - id: ACME Inc
    name: ACME Inc

:::
::::

See the frontmatter affiliations table for a complete list of the affiliations metadata you can use.

(author:email)=

Display author emails

An author's email is only shown if corresponding: true is also set.
If corresponding is unset, the first author in the list who has an email is chosen as the corresponding author.
To display the email icon for multiple authors, add corresponding: true to each one:

yaml
authors:
  - name: Marissa Myst
    email: [email protected]
    corresponding: true
  - name: Miles Mysterson
    email: [email protected]
    corresponding: true

You can add social media links to authors and affiliations.
To do so, use the following fields in an entry for either author or the affiliation:

(credit-roles)=

CRediT Contributor Roles

The CRediT (Contributor Roles Taxonomy) is a standardized way to represent the contributions of each author to a scholarly work.
It defines 14 roles that describe common types of contributions, such as writing, data curation, and software development.

When you add roles to your authors, they are validated against the CRediT standard and included in the AST of your built projrect.

:::{warning} MyST will remove Credit roles that are not part of the standard
Similar to other unrecognized metadata, if MyST does does not recognize a role, it won't include it in the final AST and will issue a warning.
:::

Example usage

You can define roles either at a project level or in page frontmatter.
It is particularly useful to use the extends: key for splitting author information into a separate configuration file.

Below is an example of two authors that covers all 14 CRediT roles:

yaml
authors:
  - name: Marissa Myst
    orcid: 0000-0000-0000-0001
    roles:
      - Conceptualization
      - Methodology
      - Software
      - Writing – original draft
      - Supervision
      - Project administration
      - Funding acquisition
  - name: Miles Mysterson
    orcid: 0000-0000-0000-0002
    roles:
      - Data curation
      - Formal analysis
      - Investigation
      - Resources
      - Validation
      - Visualization
      - Writing – review & editing

(other-contributors)=

Reviewers, Editors, Funding Recipients

Other contributors besides authors may be listed elsewhere in the frontmatter.
These include reviewers, editors, and funding award investigators and recipients.
For all of these fields, you may use a full author object, or you may use the string id from an existing author object defined elsewhere in your frontmatter.


Background (`docs/background.md`)


title: History and background description: High-level discussion of the MyST Markdown ecosystem, history of mystmd, other implementations like Jupyter Book and Sphinx, and explains past decisions.

This page discusses high-level questions about the MyST Markdown ecosystem, history of mystmd, various ecosystem implementations, and explains a few decisions made in the project.[^overview]

[^overview]: The MyST ecosystem overview is another excellent high-level source of information.

History of MyST Markdown and `mystmd`

MyST Markdown (Markedly Structured Text) is a markup language that builds on standard Markdown and is designed to create publication-quality documents, books, presentations, and websites written entirely in Markdown. The ExecutableBooks team received a grant from the Sloan Foundation to build, enhance, and promote a new path to document creation and publishing for next-generation scientific textbooks and lectures (Grant #9231).

The initial use case driving the development and design of MyST Markdown has been Jupyter Book, which allows you to create educational online textbooks and tutorials with Jupyter Notebooks and narrative content written in MyST. The extensions and design of MyST is inspired by the Sphinx and reStructuredText (RST) ecosystems. Jupyter Book V1 is considered a distribution of Sphinx, and builds on the Sphinx and Docutils Python packages.

MyST Markdown enables rich content generation and is a powerful textual format for scientific and technical communication with potential for broad adoption in modern publishing workflows. In 2022, the Executable Books team started work to document the specification behind the markup language, called myst-spec, this work has enabled other tools and implementations in the scientific ecosystem to build on MyST Markdown (e.g. scientific authoring tools, and documentation systems).

What is the MyST Document Engine and Myst Markdown?

The mystmd[^naming] document engine and its ecosystem of tools were developed as a collaboration between Curvenote, 2i2c and the ExecutableBooks team. In addition to building websites, mystmd can also help you create scientific PDFs, Microsoft Word documents, and presentations.

mystmd uses existing, modern web-frameworks in place of the Sphinx build system. These tools come out-of-the-box with prefetching for faster navigation, smaller network payloads through modern web-bundlers, image optimization, partial-page refresh through single-page application. Many of these features and performance improvements are difficult (if not impossible) to create inside of the Sphinx build system.

The JavaScript packages in the mystmd ecosystem also power web-native extensions, such as JupyterLab-myst, which renders MyST markup directly in JupyterLab.

How was the MyST Engine Developed?

The initial version was released by Curvenote as the Curvenote CLI under the MIT license, and later transferred to the ExecutableBooks team. The goal of the project is to enable the same rich content and authoring experiences that Sphinx allows for software documentation, with a focus on web-first technologies (JavaScript), interactivity, accessibility, scientific references (e.g. DOIs and other PIDs), and professional PDF outputs.

In June 2024, Jupyter Book was incorporated as a Jupyter sub-project, standardizing on using and stewarding the MyST document engine (mystmd). See #123.

[^naming]: Originally mystmd was called mystjs, but was changed as we matured the command-line interfaces.


Blocks (`docs/blocks.md`)


title: Blocks and Comments short_title: Blocks & Comments description: Blocks provide structural division in a MyST document using `+++`. These correspond, for example, to separate cells in a Jupyter Notebook. To add a comment, start your line with `%`.

Blocks

Blocks provide a structural division of MyST documents using +++.
There can be optional metadata associated with the block, such as "tags", "parts" or other identifiers.
This is used to attach attributes to chunks of content, or to break your page into cells for a Jupyter Notebook.

{myst}
+++ {"cell": "one"}
cell 1
+++ {"meta": "data!"}
cell 2
{tip}
To identify a [part of a document](./document-parts.md), like an abstract, use `+++ { "part": "abstract" }`, this will allow tools like the [](./creating-pdf-documents.md) to be created with the appropriate parts of information.

Document parts

You can use blocks to split your document into parts.
See for more information.

Page Breaks

You may use block metadata to insert page breaks into your PDF or docx export with +++ { "page-break": true }. This will have no impact on your MyST site build nor other static exports that disregard "pages" (e.g. JATS).

{tip}
You may also use `"new-page"` instead of `"page-break"`. This distinction only matters for $\LaTeX$ where `\newpage` and `\pagebreak` will be used, respectively.

Add classes to blocks

You may attach CSS classes to blocks in order to control their behavior.
The default MyST

(div-and-span)=

`div` and `span` elements

You can add div and span elements to serve a similar purpose.
Any classes and content that you add will be attached to the AST of your document.

For example, here's a div that uses the HTML theme grid classes to snap a div to the right:

markdown
:::{div}
:class: col-gutter-right
I'm off to the right!
:::

:::{div}
:class: col-gutter-right
I'm off to the right!
:::

Comments

You may add comments by putting the % character at the beginning of a line. This will prevent the line from being shown in the output document.

{myst}
This next line won't render, but it is in the HTML and LaTeX!
% Markdown comment line
{warning} comments only work at the beginning of lines
Note that a `%` is only a comment if it is at the beginning of a line, which is different than, for example, $\LaTeX$ where percent signs have to be escaped.
{note} comments split paragraphs
:class: dropdown
Putting a comment between items will split any preceding elements. For example, a comment between two lines of text will be broken up into two paragraphs, resulting in a margin between them:

```{myst}
a line
% a comment
another line
```

Citations (`docs/citations.md`)


title: Citations and Bibliography short_title: Citations & Bibliography description: Add academic citations to your documents easily, have hover-references and an automatically created bibliography. thumbnail: ./thumbnails/citations.png

Citations automatically show up in your site, including a references section at the bottom of the page.
These citations are able to be clicked on to see more information, like the abstract.
There are two different ways to add citations to your documents:

  1. Add a Markdown link to a DOI; and
  2. Add a BibTeX file, which can be exported from any reference manager, and adding a cite role to your content.

(doi-links)=

Link to any DOI in your Markdown files or Jupyter Notebooks by including a link to the DOI.
This will be transformed to a citation with a pop-up panel on hover like this: Cockett, 2022, and the reference information will be added to the reference section at the bottom of the page. Here's some example syntax:

md
This is a link in Markdown: [Cockett, 2022](https://doi.org/10.5281/zenodo.6476040).

To automatically insert the citation text, provide a standalone DOI link without link text:

  • [](doi:10.5281/zenodo.6476040)
  • <doi:10.5281/zenodo.6476040>

For example: https://doi.org/10.5281/zenodo.6476040.

If the BibTeX file in your project has this DOI, that citation will be used.

If the BibTex file does not have this DOI, the citation data will be downloaded from https://doi.org (and cached to a local file in the _build directory).

Clear the DOI cache with : myst clean --cache.

To modify the style of the inserted citation text: Use the styles in {ref}table-pandoc-citations (this works with or without a BibTeX file)

:::{warning} Many citations will slow down your build
If you have many citations that aren't present in a local BibTeX file, it may slow down the build process as the citation information is fetched dynamically.
:::

:::{note} Dealing with complex DOIs
:class: dropdown
If your DOI does not follow modern standards (e.g. strange characters or contains multiple /s), you must include the https://doi.org in the URL and may have to URL encode the DOI string to be recognized as a URL in Markdown.

For the DOI, 10.1175/1520-0493(1972)100<0081:OTAOSH>2.3.CO;2 there are <, ; and () characters that do not work well with Markdown URL parsing. There are two options:

  1. use the service https://shortdoi.org, which will give you a unique, persistent smaller DOI that will parse correctly, in this case https://doi.org/cr3qwn (which becomes https://doi.org/cr3qwn); or
  2. URL encode this to:
    https://doi.org/10.1175%2F1520-0493%281972%29100%3C0081%3AOTAOSH%3E2.3.CO%3B2, which also becomes https://doi.org/10.1175%2F1520-0493%281972%29100%3C0081%3AOTAOSH%3E2.3.CO%3B2[^brackets].

[^brackets]: In this case we can also optionally encode the brackets as %28 and %29. There aren't too many of these in the wild, so hopefully it isn't too bad!!

For DOIs with multiple slashes in the identifier you also have to use the full https://doi.org URL, for example, https://doi.org/10.3847/1538-4365/ac5f56 becomes https://doi.org/10.3847/1538-4365/ac5f56.

:::

Numbered Citations

The default citations are narrative, for numbered citations, these can be set in the site.options.numbered_references in your myst.yml (See ).

{code} yaml
:filename: myst.yml
site:
  options:
    numbered_references: true

Writing DOIs to BibTeX

If you encounter problems fetching DOIs from https://doi.org, for example the downloaded citation does not include all the data you expect or requests to https://doi.org are failing on an automated continuous integration platform, you may write your DOI citations to file using the MyST command:

bash
myst build --doi-bib

This will generate a BibTeX file myst.doi.bib which you may then rename, edit, and save to your project.
On subsequent builds, the DOIs will be loaded from this file rather than fetched remotely.

Including BibTeX

A standard way of including references for $\LaTeX$ is using wiki:BibTeX, you can include a *.bib file or files in the same directory as your content directory for the project. These will provide the reference keys for that project.

If you want to explicitly reference which BibTeX files to use, as well as what order to resolve them in, you can use the bibliography field in your frontmatter, which is a string array of local or remote files.
This will load the files in order specified.

yaml
bibliography:
  - my_references.bib
  - https://example.com/my/remote/bibtex.bib

The remote BibTeX can be helpful for working with reference managers that support remote links to your references.

Markdown Citations

You can add citations to any BibTeX entry using the citation key preceded by an @, for example, @author2023.
This syntax follows the pandoc citation syntax.
Multiple citations can be grouped together with square brackets, separated with semi-colons.
It is also possible to add a prefix or suffix to parenthetical citations, for example, [e.g. @author2023, chap. 3; @author1995].
To add a suffix to a narrative citation, follow the citation with the suffix in square brackets, for example, @author2023 [chap. 3].
As with a link to a DOI, you can also use the DOI directly instead of the BibTeX key.

{list-table} examples of markdown citations
:header-rows: 1
:label: table-pandoc-citations
* - Markdown
  - Rendered
  - Explanation
* - `@cockett2015`
  - @cockett2015
  - Narrative citation
* - `[@cockett2015]`
  - [@cockett2015]
  - Parenthetical citation
* - `[@cockett2015; @heagy2017]`
  - [@cockett2015; @heagy2017]
  - Multiple parenthetical citations
* - `[-@cockett2015]`
  - [-@cockett2015]
  - Show citation year
* - `[e.g. @cockett2015, pg. 22]`
  - [e.g. @cockett2015, pg. 22]
  - Prefix and suffix
* - `@cockett2015 [pg. 22]`
  - @cockett2015 [pg. 22]
  - Suffix for narrative citations
* - `@10.1093/nar/22.22.4673`
  - @10.1093/nar/22.22.4673
  - Citation using a DOI directly

Citations that are embedded in links will not be parsed as citations.
For example: @jupyter-book or https://hackmd.io/@turingway/B1OnVLe-3.

:::{note} Mixing URLs and @citations
If you have a citation directly after a URL without whitespace, it may be treated as part of the URL rather than as a citation.

For example, https://mystmd.org,@scipy2025 will treat @scipy2025 as part of the URL. To cite after a URL, add a space (See https://mystmd.org, @scipy2025) or use angle brackets (See <https://mystmd.org>,@scipy2025).

Avoid URLs with @ in square brackets. A URL with @ inside square brackets like [https://hackmd.io/@user] will be parsed as a citation, to avoid this you can escape the first bracket with a backslash \[.
:::

Citation Roles

MyST also provides a number of roles for compatibility with Sphinx and Jupyter Book V1.
To create a citation role in Markdown, use either a parenthetical or textual citation:

md
This is a parenthetical citation {cite:p}`cockett2015`.
You can also use a narrative citation with {cite:t}`cockett2015`.
You can also use a narrative citation with {cite:p}`cockett2015; heagy2017`.
You can also add prefix and suffix {cite:p}`{see}cockett2015{fig 1}`.

This is the difference between: {cite:p}cockett2015 and {cite:t}cockett2015. You can have many citation keys in a single role, by separating them with a semicolon, ;, for example: {cite:p}cockett2015; heagy2017.
Including a prefix or suffix is displayed as {cite:p}{see}cockett2015{fig 1}.

You can also include DOIs in citations (cite, cite:t, and cite:p) which will be linked in the same way as a simple Markdown link, but will match the reference style of the project.

md
This will be a citation: {cite}`10.1093/nar/22.22.4673`.

This will show as: {cite}10.1093/nar/22.22.4673.


Cite (`docs/cite.md`)


The MyST Markdown CLI (mystmd) and related packages developed in mystmd are developed by many contributors across academia and industry. To cite mystmd in publications, please use the following Zenodo reference:

The author list is created automatically by Zenodo based on GitHub contributions.

Other Publications

There are additional publications that describe the design and vision for the project:

  • Continuous Tools for Scientific Publishing: Using MyST Markdown and Curvenote to encourage continuous science practices (Scipy, 2024) [@10.25080/NKVC9349]

Code (`docs/code.md`)


title: Code and Code-blocks short_title: Code & Code-blocks description: Code and code-blocks can be used to show programming languages. thumbnail: ./thumbnails/code.png numbering: code: true

{warning}
The code blocks on this page are for **presentation** of code only, they are not executed.

For code execution, see the {myst:directive}`code-cell` directive in the execution section of the documentation.

You can include code in your documents using the standard markup syntax of ```language,
where language is the programming language for highlighting.

{myst}
```python
import matplotlib.pyplot as plt

plt.plot([1, 2, 3], [1, 2, 3], 'go-', label='line 1', linewidth=2)
```
{note}
If the language is `ipython` or `IPython3`, etc., it will be dynamically converted to `python` to ensure that it is highlighted correctly!

A list of language names supported by the  `myst-react` package is here: [HLJS language identifier strings](https://github.com/react-syntax-highlighter/react-syntax-highlighter/blob/master/AVAILABLE_LANGUAGES_HLJS.MD)

Code blocks

The above code is not a directive, it is just standard Markdown syntax, which cannot add a {myst:directive}code.caption or {myst:directive}code.label. To caption or label blocks of code use the {myst:directive}code directive.

{myst}
```{code} python
:label: my-program
:caption: Creating a TensorMesh using SimPEG
from discretize import TensorMesh

hx = [(1, 40)]
hy = [(1, 40)]

mesh = TensorMesh([hx, hy])
```

In @my-program, we create a mesh for simulation using [SimPEG](https://discretize.simpeg.xyz/).

Numbering and Highlighting

To add numbers and emphasis to lines use the {myst:directive}code directive. You can use {myst:directive}code.linenos which is a flag, with no value, and {myst:directive}code.emphasize-lines with a comma-separated list of line numbers to emphasize.
These options also work with {myst:directive}code-cell directives (see ).

{code} md
:linenos:
:emphasize-lines: 2,3
:caption: Emphasize lines inside of a `code` block.
```{code}
:linenos:
:emphasize-lines: 2,3
...

You can also set the start number using the {myst:directive}code.lineno-start directive, and all emphasized lines will be relative to that number.

{tip} docutils and sphinx compatibility
:class: dropdown

For full compatibility with Sphinx we suggest using {myst:directive}`code-block` directive, which is an alias of the {myst:directive}`code` directive. The MyST implementation supports both the Sphinx [`{code-block} directive`](https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#directive-code-block) as well as the `docutils` [{code} directive](https://docutils.sourceforge.io/docs/ref/rst/directives.html#code) implementation, which only supports the `number-lines` option.

You can use either `code` or `code-block` directive documented above or even a normal Markdown code block.
All implementations in MyST are resolved to the same `code` type in the abstract syntax tree.

Showing a Filename

Adding a {myst:directive}code.filename option will show the name of the file at the top of the code block. For example, myst.yml in the following example:

{myst}
```{code} yaml
:filename: myst.yml
project:
  title: Showing Filenames in code-blocks
```

(docs:literalinclude)=

Including Code Files

If your code is in a separate file you can use the {myst:directive}literalinclude directive (or the {myst:directive}include directive with the {myst:directive}include.literal flag).
This directive is helpful for showing code snippets without duplicating your content.
For parsing the file, see the documentation in .
For example, a literalinclude of a snippet of the myst.yml such as:

markdown
```{literalinclude} myst.yml
:start-at: project
:end-before: references
:lineno-match:
```

creates a snippet that has matching line numbers, and starts at a line including "project" and ends before the line including "references".

{literalinclude} myst.yml
:start-at: project
:end-before: references
:lineno-match:

:::{important} Paths are Relative
The {myst:directive}argument <include.arg> of a {myst:directive}literalinclude directive is the file path, which is relative to the file from which it was referenced.
:::

In {myst:directive}include.literal mode, the include directive also accepts all of the options from the code-block (e.g. {myst:directive}include.linenos).
To select a portion of the file to be shown using the {myst:directive}include.start-at/{myst:directive}include.start-after selectors with the {myst:directive}include.end-before/{myst:directive}include.end-at, which use a snippet of included text.

Alternatively, you can explicitly select the lines (e.g. 1,3,5-10,20-) or the {myst:directive}include.start-line/{myst:directive}include.end-line (which is zero based for compatibility with Sphinx).
The include directive is based on RST and Sphinx.


Commonmark (`docs/commonmark.md`)


title: CommonMark description: MyST Markdown is a superset of CommonMark Markdown, and fully supports all of the markdown that you are used to. Here we describe all of the CommonMark features that are supported.

This page provides an overview of the types of block and inline markup features supported by CommonMark and MyST, with pointers to additional content of interest. For full details on all the nuance of these features, please look at the CommonMark Spec documentation.

MyST (Markedly Structured Text) was designed to make it easier to create publishable computational documents written with Markdown notation. It is a superset of CommonMark Markdown and draws heavy inspiration from wiki:reStructuredText and wiki:pandoc. In addition to CommonMark, MyST also implements and extends unist and mdast, which are standard abstract syntax trees for Markdown. unist is part of the unifiedjs community and has many utilities for analyzing, exporting and transforming your content.

Block Markup

Headings

Markdown syntax denotes headers starting with between 1 to 6 # symbols.
For example, a level 3 header looks like:

{myst}
### Heading Level 3

Try changing the number of `#`s to change the `depth`.

(setex-headings)=

Setext Headings

An alternative syntax (called setext) is also supported for level 1 and 2 headers,
by underlining using multiple === or ---. For example:

{myst}
Heading 1
=========

Heading 2
---------
{seealso}
Reference headings by preceding headers with a `(label)=`. See [](./cross-references.md)!

Lists

Bullet points:

{myst}
- headings
- lists
  - bullets
  - numbers
- code blocks

Numbered items:

{myst}
1. quotes
2. breaks
3. links

Code

Code blocks are enclosed in 3 or more ` or ~ with an optional language name.

{myst}
```python
print('this is python')
```

Indented paragraphs are also treated as literal text. This may be used for code or other preformatted text.

{myst}
Some JSON:

    {
      'literal': '*text*'
    }
{seealso}
Create code-blocks with additional highlighting using the `code-block` directive. See more here!

% TODO: provide a link!
% TODO: myst: implement code-block

Quotes

Quote blocks are prepended with >:

{myst}
> Super profound quote

Thematic Break

Create a horizontal line in the output

{myst}
Section 1

---

Section 2
{seealso}
Thematic breaks should not be confused with MyST [block syntax](./blocks.md),
which is used to structurally separate content.

Links may be defined outside of text with a reference target (no spaces) and an optional title.

{myst}
[This is a link defined elsewhere!][key]

[key]: https://www.google.com 'a title'
{seealso}
These can be used in [](#inline-links) and are similar to [](./cross-references.md) in MyST.
This syntax is also similar to [](#footnotes).

Paragraph

Any text that does not belong to another block is simply a paragraph:

{myst}
any _text_

Valid HTML

Any valid HTML may also be included in a MyST document and rendered to HTML. However, you must set the option allowDangerousHtml: true in the MyST parser.
This option needs to be set internally in the parser, and is not exposed to the user as a command line flag or myst.yml setting.

Inline Markup

(inline-links)=

Auto-link where link itself is shown in final output:

{myst}
Search engine: <https://www.google.com>

Link with alternative text and optional title:

{myst}
[search engine](https://www.google.com "Google")
{seealso}
[](./cross-references.md) provides other ways to reference inline content.

(md:image)=

Inline images

Link to an image can be done similar to other inline links, or you may use HTML syntax to include image size, etc.

{myst}
{seealso}
[](./figures.md) provides other ways to size, label, and caption images.

Text formatting

Standard inline formatting including bold, italic, code, as well as escaped symbols and line breaks:

{myst}
**strong**, _emphasis_, `literal text`, \*escaped symbols\*, a hard\
break
{seealso}
[](./typography.md) provides other roles for subscript, superscript, abbreviations, and other text formatting.

Configuration (`docs/configuration.md`)


title: Configuration and content frontmatter short_title: Configuration and frontmatter description: Frontmatter can be set at the top of your documents and in myst.yml to change the look and feel of your content. thumbnail: thumbnails/frontmatter.png

Frontmatter allows you to specify metadata and options about how your project should behave or render.
Included in frontmatter are things like the document or project title, what thumbnail to use for site or content previews, authors that contributed to the work, and scientific identifiers like a doi.
Adding frontmatter ensures that these properties are available to downstream tools or build processes like building .

Where to set frontmatter

Frontmatter can be set in two places:

  1. The YAML header of a markdown (md) or notebook (ipynb) file (described as a "page" below)
  2. In myst.yml file. This will be applied to all content in that project (apart from "page only" fields).

See below about how these two sources of settings interact.

See also for a list of supported frontmatter settings.

See for theme settings like analytics, logos, and navigation toggles, which are set under site.options in your myst.yml.

Page-level frontmatter

In a MyST markdown file

A frontmatter section can be added at the top of any md file using --- delimiters.

yaml
---
title: My First Article
date: 2022-05-11
authors:
  - name: Mason Moniker
    affiliations:
      - University of Europe
---

In a Jupyter Notebook

Frontmatter can be added to the first cell of a Jupyter Notebook, that cell should be a Markdown cell and use --- delimiters as above.

:::{important} Install JupyterLab Myst
To have properly formatted frontmatter, you can install the jupyterlab-myst plugin for Jupyter. See the quickstart tutorial.

Without the extension installed, remember to format the contents of the section as valid yaml even though when rendered, the cell will not look well formatted in your notebook.
:::

::::{note} Using jupytext or a Markdown-based notebook?

If your Jupyter Notebook is described as a markdown file (e.g. using jupytext, or MyST), then this should be included in the frontmatter section, as shown in the examples below.

:::{tip} a Jupytext (md:markdown) example
:class: dropdown

{code}
:linenos:
:emphasize-lines: 15-16
---
jupyter:
  jupytext:
    text_representation:
      extension: .md
      format_name: markdown
  kernelspec:
    display_name: Python 3 (ipykernel)
    language: python
    name: python3
  language_info:
    name: python
    nbconvert_exporter: python
    pygments_lexer: ipython3
  short_title: images
  title: images and figures
---

You don't need a level-1 title,
it is defined in the frontmatter above already

```{code-cell} ipython3
import numpy as np
```

:::

:::{tip} a Jupytext (md:myst) example
:class: dropdown

{code}
:linenos:
:emphasize-lines: 14-15
---
jupytext:
  text_representation:
    extension: .md
    format_name: myst
kernelspec:
  display_name: Python 3 (ipykernel)
  language: python
  name: python3
language_info:
  name: python
  nbconvert_exporter: python
  pygments_lexer: ipython3
title: images and figures
short_title: images
---

You don't need a level-1 title,
it is defined in the frontmatter above already

```{code-cell} ipython3
import numpy as np
```

:::

::::

Project-level frontmatter

In a `myst.yml` file

Frontmatter fields can be added directly to any project: section within a myst.yml file. These will be applied to all pages in your MyST project. Here's an example:

yaml
version: 1
site: ...
project:
  license: CC-BY-4.0
  open_access: true

(field-behavior)=

How project and page frontmatter interact

Frontmatter can be attached to a "page", meaning a local .md or .ipynb or a "project". However, some frontmatter fields are available across an entire project, while others are only available for a given page.

The behavior of each frontmatter field is hard-coded within MyST. These are the kinds of scope that a frontmatter field can have:

page & project
: the field is available on both the page & project but they are independent

page only
: the field is only available on pages, and not present on projects and it will be ignored if set there.

page can override project
: the field is available on both page & project but the value of the field on the page will override any set of the project. If the page field is omitted or undefined, the project value will be used. If the page field has a value of null (or [] in the case of multi-item fields like authors), the page will override the project value and clear the field for that page.

project only
: the field is only available on projects, and not present on pages and it will be ignored if set there.

+++

(multiple-projects-deprecated)=

:::{warning} Multiple projects in one myst.yml site has been deprecated
A MyST site maps one-to-one to a single project.

Historically, the site.projects list could combine several projects under one site, each mounted at its own URL slug. This is deprecated due to development complexity, and we encourage composing multiple MyST sites at a higher level (e.g. DNS, file system or through custom web-applications).
You can combine multiple projects by deploying multiple myst sites next to each other, for example, using GitHub pages or web-based routing. A worked example, which is used in the Jupyter Book documentation deployment can be found here.
:::

(composing-myst-yml)=

Compose and extend configuration `.yml` files

You may separate your frontmatter into multiple, composable files. This allows you to have a single source of truth for frontmatter to re-use across multiple projects, for example math macros or funding information.

To reference other files from your main myst.yml file, use the extends key with relative path(s) to the other configuration files:

yaml
version: 1
site: ...
project: ...
extends:
  - ../macros.yml # A local file
  - https://raw.githubusercontent.com/myorg/myrepo/refs/heads/main/funding.yaml # A remote file
...other-config:
  • Each entry listed inside extends may be a relative path to a file, or a URL.
  • URLs must be direct links to files which are downloaded and cached locally.
  • The files must contain valid myst.yml structure with version: 1 and site or project keys.

List-type fields are combined, not replaced. This means, for example, you may define a single export in one file:

{code-block} yaml
:filename: small.yml
version: 1
project:
  exports:
    - format: meca

and another format in your main myst.yml file:

{code-block} yaml
:filename: myst.yml
version: 1
extends: ./small.yml
project:
  exports:
    - format: pdf

The final project configuration will include both a meca and a pdf export.

:::{note}
The fields exports and downloads are deduplicated by id, so if you wish to override a value from an inherited configuration you may assign it the same id. Other fields cannot be overridden; instead, shared configurations should be as granular and shareable as possible.
:::

Avoid using relative paths in extended configuration

Using relative paths in the configuration you're extending can lead to unpredictable outcomes, especially if the file you're extending is in a different folder or remote location.
Try to keep your "extended" configuration self-contained rather than reliant on local files.
For example, a configuration like the following won't work, because the main myst.yml file does not have the same path to the .css file listed below:

❌ won't work:

{code-block} yaml
:filename: config/small.yml
site:
  options:
    style: myfile.css
{code-block} yaml
:filename: myst.yml
extends: config/small.yml

Use remote URLs if you must point to files with "extended" configuration.
This ensures that MyST can find and include the file regardless of where the small.yml file is.

✅ will work:

{code-block} yaml
:filename: config/small.yml
site:
  options:
    style: https://github.com/jupyter-book/jupyterbook.org/raw/refs/heads/main/docs/_site/footer.md

:::{note} Use raw links to point to GitHub artifacts
If you wish to refer to a remote resource hosted on GitHub, make sure you use the "raw" GitHub link, not the link that you get when you click on that item in the GitHub UI.
For example:

This is the GitHub URL for a footer hosted on GitHub:

text
https://github.com/jupyter-book/jupyterbook.org/blob/main/docs/_site/footer.md

But this is the "raw link" that MyST can use:

text
https://github.com/jupyter-book/jupyterbook.org/raw/refs/heads/main/docs/_site/footer.md

:::

+++


Contribute Add Feature (`docs/contribute-add-feature.md`)


title: Adding a New MyST Feature short_title: Add a Feature description: This tutorial walks through the process of adding a new word-count role {word-count} to MyST as a core feature, rather than a plugin.

:::{important} Objective

The goal of this tutorial is to walk through the process of adding a new word-count role to MyST, {word-count}, as a core feature. It is also possible to extend MyST by writing a plugin, however, this tutorial covers the steps required to implement a feature that ships with MyST out-of-the-box. We will start from the very beginning of cloning the MyST repository, and finish with a working word-count role!

A full, unmerged pull request of this feature is available in #1027 to see the end-result.

:::

Cloning the Repository

The rest of this tutorial requires that you have basic knowledge of using Git and running commands in a terminal/shell (using one of the major operating systems). Although you can author a new MyST feature on any (supported) operating system, we will assume that you are using a typical Linux distribution for simplicity.

:::{tip} Unfamiliar with Git?
:class: dropdown

If you have not used Git before, the Git book is a comprehensive guide to the tool. Whilst it is recommended to develop a good understanding of Git, if you're short on time, Roger Dudler's Git Guide, should cover enough to get you started.
:::

🛠 Clone the mystmd repository

shell
$ git clone https://github.com/jupyter-book/mystmd

This will populate a new mystmd directory in the working directory with the current checkout (snapshot) of the MyST source code. This checkout may include new features that have yet to be released to the public, or new bugs that have yet to be identified!

🛠 Change to the mystmd directory

shell
$ cd mystmd

From this point in the tutorial, terminal sessions will show the current working directory before the $ prefix, excluding the path to the mystmd directory itself, for example:

shell
$ echo MyST is cool!
MyST is cool!
$ cd packages
(packages)$ echo MyST is cool!
MyST is cool!

(defining-roles)=

Defining a Role

The core specification for the MyST markup language is defined in the MyST spec. Most features in MyST should, over time, be incorporated into this specification so that consumers of MyST documents (such as myst-parser from the Jupyter Book V1 software stack) agree on the manner in which the content should be parsed and rendered. The process of adding features to the MyST Spec is more formalized, and is described in the MyST Enhancement Proposals. This tutorial does not cover updating the MyST Spec.

We should begin by asking the question "What is a role?" The spec defines roles as:

similar to directives, but they are written entirely in one line.

One such role is the {myst:role}underline role, which can be used to format text:

{myst}
The following text {underline}`is underlined`

We want to create a new word-count role that injects the total word count into a document. It should accept a format-string that allows us to format the resulting text, i.e.

markdown
This is a lengthy document ...

{word-count}`The number of words in this document is {number} words`

should become

markdown
This is a lengthy document ...

The number of words in this document is 5 words

Many of the "core" roles in mystmd (including {myst:role}underline) are implemented in the myst-roles package. Although a word-count role might not be considered a "core" feature, we will pretend it is for this tutorial. Let's start by looking at the existing {myst:role}abbreviation role in packages/myst-roles/src/abbreviation.ts

:::{tip}
You can hover your mouse cursor over the link to packages/myst-roles/src/abbreviation.ts to see the contents of the file. 🥳
:::

We can see that abbreviationRole is annotated with the type RoleSpec. This is the basic type of a role declaration defined by the MyST specification. There are a number of important fields, such as the name, alias, and body.

🛠 Add a new source file[^src] word-count.ts in the myst-roles package, and write the following.

[^src]: Source files are files that are added under the src/ directory of a package, e.g. packages/myst-roles/src/abbreviation.ts

{code-block} typescript
:filename: packages/myst-roles/src/word-count.ts

import type { RoleSpec, RoleData, GenericNode } from 'myst-common';

export const wordCountRole: RoleSpec = {
  name: 'word-count',
  body: {
    type: String,
    required: true,
  },
  run(data: RoleData): GenericNode[] {
    // TODO!
    return [];
  }
};

This defines a simple role called word-count, whose body (the raw text between the backticks in {word-count}`<BODY>`) is a string.
:::{tip} Other kinds of role body types
:class: dropdown

It is also possible to write a role whose body is an AST, using type: 'myst', e.g. the underline role. Declaring body as a MyST AST allows us to implement more complex markup, such as nesting roles-in-roles:

{myst}
The following text {underline}`is underlined and also **bold!**`

For our role, however, we are keeping things simple!
:::

With an empty run function, this role doesn't do anything! In order to determine what should our run function should do, we must understand how MyST documents are built. MyST generates a website or article from a MyST Markdown file in roughly three phases, shown in the diagram below.

:::{mermaid}

graph LR;
parse[Parse Markdown into AST]
transform[Transform AST into New ASTs]
export[Export AST to Website/Article]

parse --> transform --> export
:::

At the heart of MyST is the AST, defined by the MyST Specification, which serves as a structured representation of a document. Whilst directives, roles, and fragments of Markdown syntax are individually processed to build this AST, transforms are performed on the entire tree, i.e. over the entire document. As computing the word-count requires access to the entire document, it is clear that all of the logic of our new feature will need to be implemented as a transform. Therefore, our role definition will be very simple - generating a simple AST node that our yet-unwritten transform can later replace.

🛠 Modify word-count.ts

{code-block} typescript
:linenos:
:filename: packages/myst-roles/src/word-count.ts
:emphasize-lines: 10,11,12,13,14,15

import type { RoleSpec, RoleData, GenericNode } from 'myst-common';

export const wordCountRole: RoleSpec = {
  name: 'word-count',
  body: {
    type: String,
    required: true,
  },
  run(data: RoleData): GenericNode[] {
    return [
        {
          type: 'wordCount',
          value: data.body as string
        }
    ];
  }
};

Our new role is not-yet ready to be used. We next need to tell MyST that it should be included in the main program.

🛠 Import the role in packages/myst-roles/src/index.ts

{code-block} typescript
:filename: packages/myst-roles/src/index.ts

import { wordCountRole } from './word-count.js';

Notice the .js extension instead of .ts; it is important, this is just how typescript works.

Next, we must instruct MyST to load our role when parsing a document.

🛠 Add wordCountRole to defaultRoles:

{code-block} typescript
:filename: packages/myst-roles/src/index.ts
:linenos:
:emphasize-lines: 2

export const defaultRoles = [
  wordCountRole,
  abbreviationRole,
  chemRole,
  citeRole,
  ...
];

Finally, we should export the role from myst-roles, so that other packages may use it (should they need to!).

🛠 Add an export statement from the myst-roles package

{code-block} typescript
:filename: packages/myst-roles/src/index.ts

export { wordCountRole } from './word-count.js';

In order to try out our new word-count role, we need to build the myst application.

🛠 Install packages, build and link myst. See for more details.

shell
$ bun install
$ bun run build
$ bun run link

After running these steps, the MyST CLI (as described in ) can be used.

Create a Demo

With our custom role now included in a development build of MyST, we can see it in action. First, we'll create a playground directory in which we can build a MyST project.

🛠 Create a new demo directory, outside of the mystmd source.

shell
$ mkdir demo
$ cd demo

and add a new file main.md in which we will write the following:

{code-block} markdown
:label: main-md
:filename: demo/main.md

# Demo

This document is not very long.
{word-count}`It is {number} words long`.

🛠 Initialize a simple MyST project with:

shell
(demo)$ myst init

Once myst init has finished setting up the project, it will ask you whether to run myst start, we don't want to start the server now as we are going to create a single build.

🛠 Press {kbd}n to exit myst init[^or-kill-process]

[^or-kill-process]: If you have already started the server, use {kbd}Ctrl-{kbd}C to kill the process.

Investigate the AST

Now we can run myst build in the demo/ directory to run MyST.

🛠 Build the AST for the entire project

shell
(demo)$ myst build

MyST outputs the final AST in the _build/site/content directory. Running ls, we can see

shell
(demo)$ ls _build/site/content
main.json

The contents of main.json are the MyST AST. We can pretty-print the file with the jq utility

shell
(demo)$ jq . _build/site/content/main.json

The new wordCount node generated by our wordCount role can clearly be seen:

json
{
    ...
    {
      "type": "wordCount",
      "value": "{number} words long"
    },
    ...
}

Writing a Transform

As discussed in , the logic of our word count feature needs to be implemented as a transform, so that we can view the entire document. Most transforms in MyST are defined in the myst-transforms package, such as the image alt text transform which generates an image alt text from figure captions. Our transform will need to visit every text-like node and perform a basic word-count.

Let's make a start. First, we need to implement a function that accepts a MyST AST tree, and modifies it in-place.

🛠 Create a wordCountTransform in a new file packages/myst-transforms/src/word-count.ts

{code-block} typescript
:filename: packages/myst-transforms/src/word-count.ts

import type { GenericParent } from 'myst-common';


export function wordCountTransform(tree: GenericParent) {

}

The MyST AST is inspired by (and re-uses parts of) the MDAST specification for a Markdown abstract syntax tree. MDAST, like MyST, implements the unist specification, which has only three node types:

These nodes form the basic building blocks of any abstract syntax tree, and unist defines some utility functions to manipulate trees composed of them.

Given that we want to count meaningful words, we must look at the MyST specification to determine which nodes we need to look at. As MyST AST is a unist AST, and only Literal unist nodes can hold values, we can start by only considering Literal MyST nodes. The MyST specification contains a list of all node types, and it can be seen that there are only a few Literal types, such as Text or HTML.

To begin with, let's count the words only in Text nodes. To do this, we'll need to pull out a list of all of the Text nodes from the AST. We can use the unist-util-select package to find all nodes with a particular type.

🛠 Modify the transform to select all the 'text' nodes

{code-block} typescript
:filename: packages/myst-transforms/src/word-count.ts
:linenos:
:emphasize-lines: 1,6

import { selectAll } from 'unist-util-select';
import type { GenericParent, GenericNode } from 'myst-common';


export function wordCountTransform(tree: GenericParent) {
    const textNodes = selectAll('text', tree) as GenericNode[];
}

It's conventional to also define a "plugin" that makes it easy to include this transform in a suite of transformations.

🛠 Export a plugin to invoke the transform

{code-block} typescript
:linenos:
:emphasize-lines: 1,10,11,12,13

import type { Plugin } from 'unified';
import { selectAll } from 'unist-util-select';
import type { GenericParent, GenericNode } from 'myst-common';


export function wordCountTransform(tree: GenericParent) {
    const textNodes = selectAll('text', tree) as GenericNode[];
}

export const wordCountPlugin: Plugin<[], GenericParent, GenericParent> =
  () => (tree) => {
    wordCountTransform(tree);
  };

Now that we have the text nodes, let's split them by whitespace, and count the total of words.

🛠 Implement the logic to count the words

{code-block} typescript
:filename: packages/myst-transforms/src/word-count.ts
:linenos:
:emphasize-lines: 8,9,10,11,12,13,14,15,16,17,18,19

import type { Plugin } from 'unified';
import { selectAll } from 'unist-util-select';
import type { GenericParent, GenericNode } from 'myst-common';


export function wordCountTransform(tree: GenericParent) {
  const textNodes = selectAll('text', tree) as GenericNode[];
  const numWords = textNodes
    // Split by space
    .map(node => (node.value as string).split(" "))
    // Filter punctuation-only words
    .map(words => words.filter(word => word.match(/[^.,:!?]/)))
    // Count words in each `Text` node
    .map(words => words.length)
    // Sum together the counts
    .reduce(
      (total, value) => total + value,
      0
    );
}

export const wordCountPlugin: Plugin<[], GenericParent, GenericParent> =
  () => (tree) => {
    wordCountTransform(tree);
  };

Having computed the total number of words, let's replace our word-count nodes with text formatted with this value. In the same way that we selected all Text nodes

🛠 Select all nodes with a 'type' of wordCount

{code-block} typescript
:filename: packages/myst-transforms/src/word-count.ts
:linenos:
:emphasize-lines: 21

import type { Plugin } from 'unified';
import { selectAll } from 'unist-util-select';
import type { GenericParent, GenericNode } from 'myst-common';


export function wordCountTransform(tree: GenericParent) {
  const textNodes = selectAll('text', tree) as GenericNode[];
  const numWords = textNodes
    // Split by space
    .map(node => (node.value as string).split(" "))
    // Filter punctuation-only words
    .map(words => words.filter(word => word.match(/[^.,:!?]/)))
    // Count words in each `Text` node
    .map(words => words.length)
    // Sum together the counts
    .reduce(
      (total, value) => total + value,
      0
    );

  const countNodes = selectAll('wordCount', tree) as GenericParent[];
}

export const wordCountPlugin: Plugin<[], GenericParent, GenericParent> =
  () => (tree) => {
    wordCountTransform(tree);
  };

Now we can use the value attribute of each wordCount node to transform this into a known node type of a text node.

🛠 Change the node to text and replace {number} with the word count

{code-block} typescript
:filename: packages/myst-transforms/src/word-count.ts
:linenos:
:emphasize-lines: 22,23,24,25,26,27

import type { Plugin } from 'unified';
import { selectAll } from 'unist-util-select';
import type { GenericParent, GenericNode } from 'myst-common';


export function wordCountTransform(tree: GenericParent) {
  const textNodes = selectAll('text', tree) as GenericNode[];
  const numWords = textNodes
    // Split by space
    .map(node => (node.value as string).split(" "))
    // Filter punctuation-only words
    .map(words => words.filter(word => word.match(/[^.,:!?]/)))
    // Count words in each `Text` node
    .map(words => words.length)
    // Sum together the counts
    .reduce(
      (total, value) => total + value,
      0
    );

  const countNodes = selectAll('wordCount', tree) as GenericParent[];
  countNodes.forEach(node => {
    // Change the node type to text
    node.type = 'text';
    // Replace the number with the word count
    node.value = (node.value as string).replace('{number}', `${numWords}`);
  });
}

export const wordCountPlugin: Plugin<[], GenericParent, GenericParent> =
  () => (tree) => {
    wordCountTransform(tree);
  };

This pattern, of mutating existing nodes using a transform, is commonly used in the MyST ecosystem. If you want to keep information about the node's source, for example, to style it differently, you could store a flag on data or kind. If you are introducing a new node into the final rendered document, that will require you to add a new renderer. By staying within the existing node types, all existing renderers will work out of the box.

If we build MyST with bun run build, we'd notice that this transform never runs. Like our earlier word-count role, we need to include the wordCountTransform in the set of MyST transforms that run during building.

🛠 Add a new export line in packages/myst-transforms/src/index.ts

{code-block} typescript
:filename: packages/myst-transforms/src/index.ts
export { wordCountTransform, wordCountPlugin } from './word-count.js';

Then we must use this transform in the myst-cli package, which contains much of the myst build logic.

🛠 Import the wordCountPlugin in packages/myst-cli/src/process/mdast.ts

{code-block} typescript
:filename: packages/myst-cli/src/process/mdast.ts
:linenos:
:emphasize-lines: 3

import {
  ...,
  wordCountPlugin,
} from 'myst-transforms';

Finally, we'll use this plugin as part of the MyST transformations in the same file

🛠 Add the wordCountPlugin to the unified pipe of transformations

{code-block} typescript
:filename: packages/myst-cli/src/process/mdast.ts
:linenos:
:emphasize-lines: 5

export async function transformMdast(...) {
  ...
  const pipe = unified()
    .use(...)
    .use(wordCountPlugin);
}

Having modified all of the source files required to implement our word count feature.

🛠 Run bun run build to build the myst package

Now let's see what happens over in our demo!

🛠 In your demo directory, run myst start

shell
(demo)$ myst start

This will result in the following page with the word count that excludes its own text!

:::{figure} images/word-count-initial-result.png
:class: framed
:align: left

The result of running myst start with support for our new word-count role in our document.
:::

Contributing

The next steps to bring this into being a core feature would be adding documentation and running bun run changeset to add a description of what you have completed. You can then open a pull request, and the developers of MyST will aim to get this into MyST and released so everyone can use it!

A full, unmerged pull request of this feature is available in #1027 to see the end-result.

Thanks for your contributions! 🥳


Contribute Build Locally (`docs/contribute-build-locally.md`)


title: Installing & Building MyST Locally short_title: Installing Locally description: Install and build myst locally using Bun.

A description of getting started with development is given in the README; we will also outline the basic process here. MyST uses Bun as its package manager and JavaScript runtime; you will need to install Bun before running the commands in this section.

First, use bun in the base directory to install the MyST dependencies:

shell
$ bun install

After installing the dependencies, we can then build the MyST application

shell
$ bun run build

The build process may take a minute, as it has to build every package when run for the first time. Subsequent calls to bun run build will be faster as they cache unchanged intermediate dependencies. Finally, we need to make the myst binary that was built available to our terminal:

shell
$ bun run link

After running these steps, the MyST CLI (as described in ) can be used.


Contribute Docs (`docs/contribute-docs.md`)


title: Contribute to the MyST documentation short_title: Write Documentation

The documentation of MyST is inspired by the Diataxis Documentation Framework.

Suggest an edit to our documentation directly from the browser

The easiest way to suggest an edit to any Jupyter Book / MyST site is by using the Edit this page button. If you see something you'd like to change on a page, click this button:

{figure} ./images/contribute-docs-edit-button.png
:width: 75%
An example of the "edit this page" button in a MyST theme. Clicking this button will take you to the source page for this file in GitHub.

This will take you to the source file for that page on GitHub. Then press the "edit this page" button on GitHub, it looks like this:

{figure} ./images/contribute-docs-edit-button-github.png
:width: 75%
An example of the "edit this page" button on GitHub. This will open an interactive window where you can edit the source text.

Alternatively, you can use the github.dev web-based editor to suggest more comprehensive edits to one or more pages. To do so, in the browser URL replace github.com with github.dev[^period].

[^period]: Alternatively, you can just press {kbd}. from any GitHub source file page, and it will open a VSCode editor in-browser to let you suggest edits.

How to build the MyST guide documentation locally

The documentation in docs/ uses its own MyST plugins (docs/*.mjs) that require an install and build of the packages, so previewing it requires the full local development setup rather than a globally installed myst.

To build the MyST guide documentation:

  1. Clone this repository and build:

    text
    git clone https://github.com/jupyter-book/mystmd
    cd mystmd
    bun install
    bun run build
    bun run link  # install this version of mystmd globally
  2. Navigate to the docs folder:

    text
    cd docs
  3. Start a MyST server to preview the documentation:

    text
    myst start

The bun run docs command is a shorthand for steps 2 and 3 (see the developer guide).

Build with executable content

Some pages in the MyST documentation include executable code cells that demonstrate features. To build the documentation with execution enabled:

  1. Install the Python requirements:

    text
    pip install -r requirements.txt
  2. Build with the --execute flag:

    text
    myst start --execute

    Or for a static build:

    text
    myst build --html --execute

This will execute all {code-cell} directives in the documentation and include their outputs in the built site.

(docs:update-mystmd.org)=

How to update the live website at mystmd.org

When you change the content here or in most other MyST repositories, it will not automatically update mystmd.org. We use a custom MyST theme that aggregates content from several MyST repositories into a single website. The content for the live website mystmd.org is hosted by Curvenote. To update the content of mystmd.org, dispatch the action on main.

:::{warning} Only update the live deployment if we know the theme still works!
If we've only changed content in a repository, then it is usually safe to deploy the change to the live site at mystmd.org. However, if MyST or one of its themes has been updated, first confirm that the mystmd.org theme has been updated to reflect these new changes.
:::

How does the MyST guide content relate to the documentation at mystmd.org

The documentation in this repository serves the content that lives at mystmd.org/guide.

The full content and theme for mystmd.org is hosted at jupyter-book/mystmd.org, which pulls in the content from this folder along with several other documentation sources into one place.

However, the documentation here can be built independently for previewing your changes.

See for more information about the mystmd.org deployment.

(about:mystmd.org)=

About the `mystmd.org` documentation and theme infrastructure

The MyST website at mystmd.org is a custom MyST theme designed by the community in order to aggregate documentation from many locations into one website.

  • The content for mystmd.org is located across several repositories in the MyST ecosystem, and it is deployed via a Curvenote deployment that uses the latest version of our custom mystmd.org theme from Vercel.
  • The theme for mystmd.org is in a repository we manage and deployed via Vercel. (see below for details)

(mystmd-theme-location)=

Where is the mystmd.org theme located?

The jupyter-book/mystmd.org repository has a custom MyST theme that is used to control the content and structure of the site at mystmd.org. It is a Remix website and is deployed on Vercel.

How does the mystmd.org theme work?

The custom MyST theme at jupyter-book/mystmd.org is an example of generating a custom application/site using MyST components. It is not how most people build websites with MyST Markdown, but is a good "Advanced Use" example.

The MyST documentation uses a custom theme that inherits from a chain of base themes.
Here's a brief overview of where to look for things:

The content of mystmd.org is pulled from a number of repositories across the jupyter-book organization.

For example, mystmd.org/guide is pulled from jupyter-book/mystmd: /docs/, and mystmd.org/jtex is pulled from jupyter-book/mystmd: /jtex/docs/. It also includes some custom applications like the Sandbox and MyST demo on the landing page.

How do I deploy changes to the mystmd.org theme?

After updating the theme at mystmd.org, we must deploy it to Vercel in order to make it available for use.
We use a GitHub Action to deploy to our Vercel project. This will update the theme infrastructure used to power mystmd.org.

To trigger that action, you can either:

  1. Trigger a workflow dispatch. Go to the Vercel action page. Click on Run Workflow and it will run.
  2. Push a commit to main (of jupyter-book/mystmd.org). Any new commit to the main branch will trigger this action.

(mystmd-org-update-packages)=

How do I update the myst package versions used by the mystmd.org theme?

The mystmd.org theme explicitly imports myst packages and pins their versions in theme/package.json. When we release new versions of these packages, the theme must be updated manually to pull them in.

To update the package versions:

  1. Clone jupyter-book/mystmd.org locally.
  2. Update the myst package versions in theme/package.json. Use the VSCode Version Lens extension to show the latest available version inline for each dependency.
  3. Run npm install to install the updated packages.
  4. Run npm run dev to verify the site works with the new versions.
  5. Commit and push to main. This triggers the Vercel deploy action automatically.

(team-vercel-deployment)=

Where is the Vercel deployment that serves the mystmd.org theme?

Currently the theme is run on Rowan's personal Vercel project, the deployment is completed in the GitHub Actions and access to this is only needed if the GitHub Action does not deploy.

How do I preview content changes in pull requests?

We use the Netlify service to generate deploy previews of the mystmd documentation for all pull requests.
These build only the mystmd guide (hosted at https://mystmd.org/guide), not the entire mystmd.org website.
They're just used for convenience and review purposes.

Configuration for our Netlify build exists in the netlify.toml configuration file in the root of the repository.

Any team members can have Developer access to our shared Netlify account, and Steering Council members can have Owner access.
If you'd like access, please ask a maintainer.

(jb-vs-md)=

How to choose what to document in jupyterbook.org vs. mystmd.org?

:::{warning} We're still figuring this out!
This is a best-effort description of our approach to documentation, based on some conversations we had in https://github.com/jupyter-book/jupyter-book/issues/2239. We'll re-assess this as the documentation of each project evolves.
:::

Jupyter Book and the MyST Document Engine have heavily overlapping functionality, so it may be unclear whether something should be documented at mystmd.org or jupyterbook.org. That's OK and expected - here are some guidelines for where to document things:

  • The MyST document engine will be a power user tool. It will be more flexible and modular, with an extensive plugin ecosystem. It will be agnostic to build output, and single- or multi-page documents.
    • MyST should have the complete reference documentation for the MyST engine, as well as longer explanatory content about the MyST ecosystem.
    • As functionality is moved into plugins, we similarly prioritize reference documentation and explanation in those spaces.
    • MyST should be a standalone tool and have enough information for a power user to use on its own.
  • Jupyter Book will be a tool for typical users focused around multi-page documents and websites. It will be opinionated, focused around the "book themes", and be more accessible to a new user or someone unfamiliar with JavaScript workflows.
    • Jupyter Book should focus on How-Tos and Tutorials that are driven by use-cases in multi-page workflows (e.g., documentation, books, community websites, etc).
    • Focus on keeping documentation outcome-oriented, and link heavily to the MyST engine docs for more complete reference information and explanation.

How to create and edit Excalidraw diagrams

We use Excalidraw diagrams throughout our documentation.
This is a lightweight way to create flow charts and diagrams.

(export-excalidraw)=

To create a new Excalidraw diagram, first create what you wish in Excalidraw. Then, take the following steps:

  1. Click the hamburger menu, then export image.
  2. Ensure that embed scene is checked. This will include Excalidraw metadata with the output for future editing.
  3. Click the SVG button (or PNG if necessary). This will download a local file with the diagram.
  4. Put that in the images/ folder and link it from your content.

To make edits to an Excalidraw diagram, follow these steps:

  1. Open excalidraw.com.
  2. Find the file that you want to edit.
  3. Confirm that it is an SVG or PNG that was created with Excalidraw, and that had embed scene checked upon creation.
  4. Drag-and-drop that file into the Excalidraw window. (or, click hamburger menu -> open and add your file that way).
  5. Make your edits in Excalidraw.
  6. Export the new diagram using the steps described above.
  7. Replace the old file with the new one and commit it to git. If using an SVG, you can also just copy/paste the new SVG text and replace the old SVG text with it.

How to enable Pull Request previews on Netlify

Netlify Deploy Previews allow others to preview the documentation for a repository as part of the Pull Request workflow. This will add a Documentation Preview to the Status Checks of a pull request.
To enable it, do the following:

  1. Enable Netlify builds for your repository.
  2. Go to the configuration -> notifications section and look for emails and webhooks.
  3. Go to Deploy Notifications, which lists the notifications that will post to each PR.
  4. To add a new notification, click Add Notification -> GitHub Commit Status.
  5. Ensure the following notifications are listed for deploy state commit checks:
    • Add deploy state commit checks when Deploy Preview starts
    • Add deploy state commit checks when Deploy Preview succeeds
    • Add deploy state commit checks when Deploy Preview fails

How to modify and release a MyST theme

Below is a brief description for how modifications in a theme are released for public consumption.

  1. [dev facing] we make a bunch of changes to the theme, probably changing things in themes/book/*, themes/article/* and packages/*
  2. [dev facing] if there were changes in packages/* we release the @myst-theme/* packages (changesets based ci step)
  3. [dev facing] we then run make deploy-book and make deploy-article which builds and bundles each theme, making commits to the https://github.com/myst-templates/book-theme and https://github.com/myst-templates/article-theme repos.
  4. [user facing] at that point those latest bundle commits will be what is pulled by mystmd clients - people who already have those downloaded need to myst clean --all (as we don't yet auto bump based on version changes, see #854 for updates).

Contribute Templates (`docs/contribute-templates.md`)


title: Contribute a LaTeX, Word or Typst Template short_title: Create a Template description: Create a MyST Template so others in the MyST community can make use of your work and potentially improve it. There is an API to list all templates, and when you contribute it, your template will show up in command line queries, like listing all `myst templates`.

The MyST templates organization curates a community collection of MyST Markdown compatible templates. These templates allow MyST to export markdown files as typeset, formatted documents using PDF, LaTeX, Word or Typst templates. These templates expose data-driven options for customization ensuring the final documents comply with author submission guidelines provided by a particular journal, conference organizer or university.

Creating a Template

To create a template, you can follow the guide, as well as see the video below. Note that these guides are currently written for LaTeX, but can be generalized for Word and Typst. Documentation updates welcome!

{note} see the video tutorial 📺
:class: dropdown
```{iframe} https://www.youtube.com/embed/-oD6jlM23wY
:width: 100%
```

Contributing a Template

MyST welcomes contributions of new templates, or improvements to any of our curated templates. To get started with LaTeX:

  • 📝 open an issue - tell us about the template you'd like added. Provide a link to the template if it's online, or attach the files if you have them.
  • 🏋🏽‍♀️ create your own template - Learn how to create your own template in a few easy steps!
  • 🤝 contribute a template - Even if it's partial or barely started we'll help test and get it over the line.

For more details on contributing, learning about the structure of templates specifically and how to build one. See the Contributors Guide, currently the documentation is written for creating a LaTeX templates but you can follows the same approach for any MyST Template or see MyST templates organization for a range of examples.


Contributing (`docs/contributing.md`)


title: Contribution Guide short_title: Contribution Guide

This page contains pointers and links to help you contribute to this project.

Developer guide

Technical detail about project architecture and contributing code is given in the developer guide

Our team compass

The Jupyter Book Team Compass is a source of truth for our team structure and policy.
It also has a lot of information about how to contribute.

Code of conduct

We expect all contributors to this project to Code of Conduct.

AI policy

It is important for us that contributions are correct, that the author
understands their submission, and to prioritize human interactions in
GitHub communications. With this in mind, please:

  • Follow our contribution workflow especially if you're using an LLM to generate code.
  • Carefully review (with your own eyes) and test any LLM-generated code before submitting.
  • Write PR descriptions by hand (fine to use some AI text below yours, if clearly delineated, or to use an LLM for a grammar check).
  • Respond to questions in person (do not paste LLM feedback).
  • Disclose the tool used. This is not punitive (some team members use LLMs too): it better informs the review process.

As a rule of thumb: the more effort and thoughtfulness you put into your contribution, the more likely maintainers are to review, discuss, and iterate.

Where we work

We do most of our work in GitHub repositories in the jupyter-book/ GitHub organization.

What should I work on?

Our roadmap provides a big picture overview of project initiatives.
Current priorities track work items that we would like to complete soon.
You will see these divided into two lists: "Priority" items (important and urgent) and "Side Quest" items (important, not urgent).
If you are unsure what to work on, we recommend you pick an item from one of these two lists.
These items are most likely to get an enthusiastic response, and will likely be reviewed quickly.

Where we communicate

Relevant GitHub repositories

The mystmd project covers a subset of the jupyter-book/ GitHub organization.
It focuses on the JavaScript-based MyST Markdown engine and ecosystem, as well as the markdown syntax that MyST uses.

Below is a list of relevant repositories and a brief description of each.

  • mystmd: The MyST document engine and functionality not related to specific renderers.
  • myst-theme: The web components and themes that are used for either the book or article themes for MyST.
  • myst-spec: Questions about the markdown syntax for MyST and standardization efforts for MyST functionality.
  • jupyterlab-myst: Questions about the JupyterLab extension for MyST.
  • MyST Templates: Repositories that contain templates for rendering MyST documents into various outputs like LaTeX, JATS, Typst, and Docx.

[!NOTE]
There are many repositories with similar functionality in the executablebooks/ organization. Many of these are based around the Sphinx documentation ecosystem. For example, the MyST-NB repository is a Sphinx extension for Jupyter notebooks, and the MyST Parser repository is a MyST markdown parser for Sphinx.

(contributing:workflow)=

Contribution workflow

Generally speaking, our contribution workflow looks something like this:

  • Conduct free-form conversation and brainstorming in our forum. We have a community forum for general discussion that does not necessarily require a change to our code or documentation. If you have a specific enhancement or bug you would like to propose for resolution, see the next steps.
  • Search open issues to see if your idea is already discussed. Use a GitHub search in the jupyter-book/ organization to see if you should add to an existing issue or create a new one. If you don't think an issue exists that covers your idea or bug, go ahead and open one.
  • Discuss and propose changes in issues. Issues are a way for us to agree on a problem to solve, and align on a way to solve it. They should invite broad feedback and be as explicit as possible when making formal proposals.
  • Make a pull request to implement an idea. We use Pull Requests to formally propose changes to our code or documentation. These generally point to an issue and ideally will close it.
  • Iterate on the pull request and merge. Pull Requests should have discussion and feedback from at least one core team member, and ideally from many. Once the PR is ready to merge, a core team member may decide to do so. See our decision-making guide for formal details.

This describes the high-level process that is usually followed.
In practice, we recommend attempting a contribution to get a feel for how it works in practice.

How our team is structured

Our Team page lists all of the teams in the jupyter-book/ organization and their members.
In addition, our Governance page describes the responsibilities and authority that team members have.

How we make decisions

Our governance page describes our formal decision-making processes.


Creating Citation Cff (`docs/creating-citation-cff.md`)


title: Exporting CITATION.cff description: MyST can be used to generate a `CITATION.cff` file for your project, reducing duplication of citation information.

The Citation File Format is a human-readable format for declaring citation information, particularly for software and datasets. By adding a CITATION.cff file to your project, other researchers know exactly how your project should be cited. This makes authors' lives easier, improves citations to your project, and enables reproducibility by pointing readers directly to the software/data repository. The Citation File Format is supported by GitHub, Zenodo, and Zotero.

In MyST, much of the citation information is already present in the myst.yml file. Rather than duplicating information between myst.yml and CITATION.cff, you may instead export CITATION.cff from MyST. This behaves like a standard export:

yaml
version: 1
project:
  ...
  exports:
    - cff

There are fields in the Citation File Format that are not present in myst.yml. To include these, you may add them to your export. Available fields are described in the citation-file-format repository. For example:

yaml
version: 1
project:
  ...
  exports:
    - format: cff
      date-released: '2021-07-18'
      commit: b49bf7feb3913b15a29cdc8c246e75df4ed4def9

Creating Jats Xml (`docs/creating-jats-xml.md`)


title: Working with JATS description: MyST can be used to create JATS (Journal Article Tag Suite), a NISO standard that is widely used in scientific publishing.

In the context of open access scientific publishing, it is common[^openaccessjats] that the Version of Record (VoR) includes the full text of the article as well as figures and tables. Scientific metadata about an article is no longer just the abstract, authors, and references. This full text, structured data that is permissively licensed will open up many workflows for scientific research. The full text is most often stored in an XML format which can be used to create all other views of this content (PDF, HTML). The structure of this XML varies between publisher, with the most used open standard being JATS (Journal Article Tag Suite), a NISO standard.

[^openaccessjats]: PubMedCentral has about 8.5 million full-text articles in JATS. There are currently 5.8 million works with full-text XML available through CrossRef, for example. See CrossRef’s API.

JATS defines a set of XML elements and attributes designed to represent journal articles in a single standard XML format. A JATS document is a single XML file that includes an article, which has front matter (authors, affiliations, title, abstract, funding), the body of the article (all sections, text, references to figures/images, tables), and the back matter (appendix sections, data-availability statements, conflict statements, acknowledgments, and the reference list). There can also be a number of sub-articles (with their own stubbed, frontmatter) that can be used to store other documents in a project (reviews, responses, appendices, or even computational notebooks!).

MyST can be used to both download, read, create the full JATS XML, using our tool called jats-xml (see install instructions). To download a JATS file, for example, you can use the command-line interface:

bash
jats download https://elifesciences.org/articles/81952 article.jats

You can also summarize a JATS article, either locally or remotely using a DOI or some URLs.

bash
jats summary https://elifesciences.org/articles/81952
{figure} images/jats-output.png
Summarize the contents of the JATS, given a URL, DOI, or local file. This will list the various components, and can be read by MyST.

JATS for Journal Archiving and Interchange

There are three different tag-sets for JATS, as a lot of the content that we are aiming to work with requires computational notebooks and multiple articles we have chosen to focus MyST on the Journal Archiving and Interchange (v1.3). There is a lot of overlap between the tag-sets, with the Journal Archiving and Interchange tag-set being the most permissive and allowing, for example, sub-articles. Try clicking the "JATS" button in the following demo.

{myst}
---
numbering: | # This has to be a string for now, sillyness in the docutils clone
  heading_3: true
  enumerator: 5.3.%s
---

(jats-sec)=
### Looking at JATS

JATS body content is XML: **strong**, _emphasis_, with cross-references ([Sec. %s](#jats-sec)) as an `xref`.

The largest structural difference in body content is that the sections (sec) are nested, which are accomplished as specific transforms for this export target.


Creating Pdf Documents (`docs/creating-pdf-documents.md`)


title: Create a PDF description: Export to over 400 journal templates from a MyST Markdown file, which uses LaTeX and can create print-ready, multi-column, professional PDF documents.

MyST can create a PDF for print-ready scientific papers or books.
It does so by first rendering your MyST document into $\LaTeX$ or Typst and then using those engines to create a PDF.

Myst uses templates to allow you to control the look and feel of the final PDF output. The MyST Templates organization contains templates for rendering MyST documents into the structure of over 400 journals.

:::{seealso}
In addition to PDF, you can also render your documents as Beamer presentations or as Microsoft Word to share with other collaborators.
:::

{figure} ./images/pdf-exports.png
:label: fig-export-to-pdf
:width: 100%

Export to over 400 journal templates from a MyST Markdown file, which uses $\LaTeX$ and can create print-ready, multi-column, professional PDF documents.

(myst-documents-tutorial-card)=
:::{card} MyST Documents Quickstart Tutorial 📑
:link: ./quickstart-myst-documents.md
See the quickstart tutorial for getting started with exporting to Word documents, $\LaTeX$ and PDFs with various templates.
:::

How to export to PDF

To create a new pdf export type for your MyST document, add an exports list to either your document frontmatter or your myst.yml configuration file.

(export-frontmatter-pdf)=

{code-block} yaml
:filename: article.md
---
title: My PDF
exports:
  - format: pdf
    template: arxiv_two_column
    output: exports/my-document.pdf
---

To build the exports, use the myst build command, which will work with your project structure if it exists and create a document in the output path that you specify.

bash
myst build my-document.md --pdf

Based on the output field in the export list in the frontmatter, the PDF and a log file will be written to exports/my-document.pdf and any associated log files. If the output file is a folder, the document name will be used with a .pdf or .tex extension, as appropriate. Any necessary auxiliary files (e.g. for example *.png or *.bib) will be added to the base folder (exports/ above).

(render-latex)=

Rendering PDFs with $\LaTeX$

{danger}
:class: dropdown
# PDF exports require $\LaTeX$ or Typst to be installed

The default PDF renderer uses $\LaTeX$ to create PDFs, which means that to work locally you will need to [](#install-latex). A warning will occur if MyST cannot find a $\LaTeX$ environment, as well as forcing the build process and reporting any errors.

As an alternative, for faster PDF builds, you may use [Typst](#rendering-pdfs-with-typst) instead.

The rendering process for scientific PDFs uses $\LaTeX$ and makes use of the jtex templating library, to convert to $\LaTeX$ the myst-to-tex packages is used. The libraries work together for sharing information about frontmatter (e.g. title, keywords, authors, and affiliations).

{mermaid}
flowchart LR
  A[Jupyter Notebook] --> C
  B[MyST Markdown] --> C
  C(mystmd) --> D{AST}
  D --> E[jtex] --> G[LaTeX]
  D --> F[myst-to-tex] --> G
  G --> H[PDF]

To include the $\LaTeX$ source code as well as the included images, change the format to:

tex
: create a folder containing the $\LaTeX$ source code, referenced images, citation files, and any class files required by the template.

tex+pdf
: create a folder containing all the same outputs as tex as well as run the build process for the pdf.

pdf
: Run the above processes in a temporary folder, and only copy in the PDF export and any log files if there are problems.

(install-latex)=

Install $\LaTeX$

See the official documentation for installation instructions for $\LaTeX$ at:

Ensure that you download a full distribution with appropriate libraries installed.

% Probably a note in the future about running this remotely?

(rendering-pdfs-with-typst)=

LaTeX export gotchas

By default, MyST-rendered PDFs will use the \section command as the top-level heading in LaTeX. This is standard for scientific papers and short reports. However, if you are writing a book or long document (e.g. with Jupyter Book), this may cause errors as LaTeX expects the \chapter or \part command to be top-level. MyST can accomodate for this difference by manually setting the level key to each file you wish to export, as per the table below:

Level Corresponding LaTeX command
-1 \part
0 \chapter
1 \section
2 \subsection
3 \subsubsection

For instance, assuming you had the following directory structure:

text
.
├── chapter-one
│  ├── index.md
│  ├── section-one.md
│  └── section-two.md
├── chapter-two
│  ├── index.md
│  ├── section-one.md
│  └── section-two.md
├── index.md
└── myst.yml

And the following table of contents in your myst.yml:

yml
toc:
  - file: index.md
  - file: chapter-one/index.md
    children:
      - file: chapter-one/section-one.md
      - file: chapter-one/section-two.md
  - file: chapter-two/index.md
    children:
      - file: chapter-two/section-one.md
      - file: chapter-two/section-two.md
# ... rest of your config

By default, MyST will assign chapter-one/index.md with the \section command in LaTeX, instead of the \chapter command. To be able to change this behavior, you will need to set the level key for every page, as follows:

yml
exports:
  - format: pdf
    template: plain_latex_book
    output: my_book.pdf
    articles:
      - file: index.md
        level: 0
      - file: chapter-one/index.md
        level: 0
      - file: chapter-one/section-one.md
        level: 1
      - file: chapter-one/section-two.md
        level: 1
      - file: chapter-two/index.md
        level: 0
      - file: chapter-two/section-one.md
        level: 1
      - file: chapter-two/section-two.md
        level: 1

This will render the chapters using the \chapter command, ensuring it is consistent with LaTeX's book rendering.

Rendering PDFs with Typst

Typst is a markup-based typesetting language. It is significantly faster and simpler than using $\LaTeX$ with results of equal or better quality.

(typst:install)=

How to install Typst

Follow the Typst installation instructions for several options to install Typst.
We strongly recommend using the latest releases of Typst. If you get a confusing Typst error, a good first step is to upgrade your version of Typst.

:::{warning} Do not use npm to install Typst
The version of Typst on npm (or similar community-managed installation services) is often out-of-date, and we recommend following the Typst instructions directly.
:::

How to render PDFs with Typst

To render Typst PDFs locally, you must first install Typst.

Then add Typst to your export targets. Add format: typst and select a Typst template. Below is an example that also defines the output PDF to generate:

{code-block} yaml
:filename: article.md
---
title: My PDF
exports:
  - format: typst
    template: lapreprint-typst
    output: exports/my-document.pdf
---

Finally, build the PDF output with Typst using the following command:

bash
myst build article.md --typst

You can use document frontmatter to control various aspects of your Typst outputs.
The Typst templates use the MyST templating library and support the same configuration as $\LaTeX$.

Choosing a Template

There are currently 422 journals supported[^journals] and it is straight forward to add new personal templates, or contribute them back to the community.

[^journals]: As of September 15, 2022.

text
This is the total number of _journals_ that can be created from MyST, which is a higher number than the number of _templates_, as some templates support many different journal exports. As we add more templates we will probably switch this number to templates, which is closer to 15, but that doesn't sound as impressive out of the gate. 🚀

Templates exist for both $\LaTeX$ and Typst builds. To list all of the public templates, use the myst templates command:

bash
myst templates list --pdf --tag two-column

> arXiv (Two Column)       arxiv_two_column
> Description: A two column arXiv compatible template
> Tags: paper, two-column, preprint, arxiv, bioarxiv, eartharxiv
>
> ...

Once you have found a template, you can list detailed information about the parts and options that the template exposes using:

bash
myst templates list arxiv_two_column --tex

> ID: public/arxiv_two_column
> Version: 1.0.0
> Author: Brenhin Keller
> Description: A two column arXiv compatible template
> Tags: paper, two-column, preprint, arxiv, bioarxiv, eartharxiv
>
> Parts
> abstract (required) - Keep it short — abstracts longer than 1920 characters will not be accepted ...
>
> Options
> line_numbers (boolean) - Turn line numbers on in the PDF

There are two ways to provide information to a template, through parts and options.

Template `parts`

The parts of a template are things like abstract, acknowledgments or data_availability, see for more information. These parts are usually written pieces of a document, but are placed specifically in a template. For example, an abstract usually has a place in templates, with a box or other typographic choices applied. These parts can be marked as required, and will raise error in the PDF export process, however, myst will always try to complete the build.

A part of a template can be defined using the page frontmatter or metadata on a block. An example of using the frontmatter is:

{code-block} yaml
:filename: article.md
---
abstract: |
  MyST (Markedly Structured Text) is designed to create publication-quality documents
  written entirely in Markdown. The markup and publishing build system is fantastic,
  MyST seamlessly exports to any PDF template, while collecting metadata to make your
  writing process as easy as possible.
---

Template `options`

Template authors should choose to use standard frontmatter properties where possible, however, all templates can also expose custom options through their . Include options for the build in the exports list. For example, to turn on line_numbers in the template, add the option to the dictionary.

{code-block} yaml
:linenos:
:emphasize-lines: 7
:filename: article.md
---
title: My PDF
exports:
  - format: pdf
    template: arxiv_two_column
    output: exports/my-document.pdf
    line_numbers: true
---

Any unrecognized, or malformed entries will be logged as errors as well as required options that are not provided.

Use `extend` to set options

Using extend configuration you can create an export.yml file which is referred to in the myst.yml file. For example, this configuration tells myst.yml to look for an export.yml file for configuration:

{code-block} yaml
:filename: myst.yml
---
extends:
  - export.yml
---

And the export.yml file defines the export configuration to use in the project:

{code-block} yaml
:filename: export.yml
---
version: 1
project:

  downloads:
    - id: output-pdf1

  exports:
    - id: output-pdf1
      format: typst
      template: https://github.com/myst-templates/plain_typst_book
      output: book.pdf
      # additional options

      #### Cover picture
      cover: Cover.PNG
      #Coverposition:
      # coverwidth:

      #### Logo at top of position
      logo: logo.svg
      logo_width: 10
---

Creating a Template

The export list can also point to local templates, for $\LaTeX$ these are built using jtex, and you can learn more about how to create a template for: any $\LaTeX$ document and Beamer presentations.

To make use of the template locally, validate it using jtex check and then point to the template folder in your export:

{code-block} yaml
:filename: article.md
:linenos:
:emphasize-lines: 5
:caption: The template can be a path to a `jtex` template, which contains a `template.yml` and `template.tex` as well as any other `cls` or `def` files.
---
title: My PDF
exports:
  - format: pdf
    template: ../templates/my-template
    output: exports/my-document.pdf
---

Please consider contributing your template to the growing list of templates so that other people can benefit and improve your work!

Excluding Content from Specific Exports

If you have a block or notebook cell that you do not want to render to your $\LaTeX$ output, add the no-tex tag to the cell. Similarly, to exclude a cell from Typst, use no-typst. To exclude a cell from both formats, use no-pdf.

markdown
+++{"no-pdf": true}
This will not be in the pdf
+++

Including Content with Specific Exports

If you need to inject some $\LaTeX$- or Typst-specific content into their respective exports, you may use the {raw:latex} or {raw:typst} role and directive. For example, to insert a new page in Typst with two columns:

markdown
```{raw:typst}
#set page(columns: 2, margin: (x: 1.5cm, y: 2cm),);
```

The content in these directives and roles will be included exactly as written in their respective exports, and will be ignored in all other contexts.

(multi-article-exports)=

Multi-Article Exports

Sometimes you may want to combine multiple MyST documents into a single export, for example a thesis or a book. MyST makes this possible with multi-article exports for PDFs built with either $\LaTeX$ or Typst.

For perform a multi-article export, add multiple articles to the export frontmatter:

{code-block} yaml
:filename: article.md
---
title: My PDF
exports:
  - format: pdf
    template: plain_latex_book
    output: exports/my-thesis.pdf
    articles:
      - introduction.md
      - project-one.md
      - project-two.md
      - conclusions.md
---

As an alternative to listing articles in MyST frontmatter, you may specify a table of contents using the Jupyter Book V1 format:

{code-block} yaml
:filename: article.md
---
title: My PDF
exports:
  - format: pdf
    template: plain_latex_book
    output: exports/my-thesis.pdf
    toc: thesis_toc.yml
---

By default if no articles are given, exports defined in page frontmatter will produce a single-article export from of that page, and exports defined in the myst.yml project configuration will produce a multi-article export based on the project structure.

Custom Frontmatter for Exports

Export frontmatter may differ from page or project frontmatter. For example, you may with to give your export its own title, which does not match the project title. To do so, add the alternative frontmatter to your export:

{code-block} yaml
:filename: article.md
---
title: My Interactive Research!
exports:
  - format: pdf
    title: My Static Research as a PDF
    output: exports/my-document.pdf
---

You may redefine any frontmatter fields. These redefined fields will replace the values found in page frontmatter and myst.yml project configuration.

Further, for , you may redefine frontmatter for every specific page. To do so, you must use a list of article objects (as opposed to a _toc.yml file or a list of article names):

{code-block} yaml
:filename: article.md
---
title: My PDF
exports:
  - format: pdf
    title: My Thesis
    date: 10 May 2023
    template: plain_latex_book
    output: exports/my-thesis.pdf
    articles:
      - file: introduction.md
        title: Introduction to This Thesis
      - file: project-one.md
      - file: project-two.md
      - file: conclusions.md
        title: Summary of this Thesis
---

Creating Word Documents (`docs/creating-word-documents.md`)


title: Microsoft Word description: Export to Microsoft Word directly from MyST Markdown.

You can render your MyST documents as Microsoft Word documents.

{figure} ./images/word-export.png
:label: fig-export-to-word
:width: 50%

Export to a Microsoft Word document to easily share with your colleagues.

Exporting to Word

To create a new docx export type for your MyST document, in your document frontmatter, add an exports list:

(export-frontmatter-word)=

yaml
---
title: My Document
exports:
  - format: docx
    output: exports/my-document.docx
---

To build the exports, use the myst build command, which will work with your project structure if it exists and create a document in the output path that you specify.

bash
myst build my-document.md --docx

Based on the output field in the export list in the frontmatter, the PDF and a log file will be written to exports/my-document.docx.

{warning}
:class: dropdown
# When opening, click **Yes** to "Accept Links"

The default export creates links for cross references and citations, and the first time this is opened Microsoft Word asks you if you would like to link these. **Click Yes**.

```{figure} ./images/export-word-link.png
:label: export-word-link
:width: 40%

Allow word to fix links for cross-references and citations.
```

Math and Equations

Currently MyST export does not fully create math in Word's format, instead, $\LaTeX$ is used, which can easily be converted inside of Word.

  1. Select any equation and open the equation toolbar
  2. Click LaTeX
  3. In the convert dropdown, select "All - Professional"
  4. Click convert
{figure} ./images/convert-word-equations.png
:label: convert-word-equations
:width: 100%

To fix equations in Word, use the equation toolbar to select `LaTeX` and from the dropdown select, all professional, then click convert.

Rendering Word with `myst-to-docx`

The rendering process for word documents uses the myst-to-docx package. The library works together with mystmd for sharing information about frontmatter (e.g. title, keywords, authors, and affiliations).

{mermaid}
flowchart LR
  A[Jupyter Notebook] --> C
  B[MyST Markdown] --> C
  C(mystmd) --> D{AST}
  D --> E[myst-to-docx]
  E --> G[docx]

Word Templates

We are looking for contributions in this area to improve Microsoft Word export, please reach out if you want to help!


Cross References (`docs/cross-references.md`)


% Based loosely on https://jupyterbook.org/content/references.html

References refer to labeled content (e.g. a figure, document or table) and automatically generates links and extra information, like numbering. This page covers the basics of setting up references to content and shows examples for sections, figures, tables and equations.

{seealso}
See [](./external-references.md) to connect your <wiki:documents> to external [linked](wiki:Hyperlink) content like <wiki:Wikipedia>, or other MyST projects. These cross-references allow for [hover](wiki:Hovercraft)-references to external content.

See [](./citations.md) to cite scholarly work and create bibliographies.

Reference syntax

There are several ways that you can define cross-references with MyST.
Below are the major ones.

(link-references)=

Cross-referencing content is accomplished with markdown link syntax ([text](#target)) where #target is the target label[^1], like the figure, equation or section header that you are referencing. If you leave the text empty, MyST will fill in the link with the title, caption, document name, or equation number as appropriate (e.g. "Figure 1" or "Section 1.3.7"). If you do supply text, you can control what is displayed in the reference, as well as have access to placing the name and enumerator of the target, using {name} and {number}, respectively[^2].

[^1]: Note that targets without the # will resolve, however, they throw a deprecation warning. By including the # there is a better chance of your content working in other markdown renderers like GitHub or VSCode.
[^2]: Note that not everything has a number or name (e.g. a paragraph usually isn't numbered, and an equation doesn't have text to resolve into {name}). An unnumbered node will resolve to ??, and raise a warning if they are not defined. If no {name} is defined the node label will be used instead.

text
/* Detailed source-code truncated for AI context efficiency. */

[^no-toc]: To link to a page, that file must be included in the project's table of contents. If it isn't, then the document will be presented as a static file.

% TODO: absolute links

Reference using a shorthand `@` syntax

You can reference targets with a short-hand using the @ symbol, like so:

markdown
@target

This cuts down on the amount of markdown that is needed to create the reference.
For example, the following text:

md
Other ways to reference are @link-references and the @ref-role role.

Results in:

Other ways to reference are @link-references and the @ref-role role.

:::{note} This is a short-hand for several types of references
The @ short-hand syntax for referencing maps on to many different types of referencing roles, depending on the target that you are referencing.

For example, if @target is a bibliography entry, it will map onto the {myst:role}cite role.
If it is an image or a section header, it will map on to the {myst:role}ref role.
:::

(reference:roles)=

Reference using MyST roles

Roles are a more explicit way of defining references that provide more configuration at the cost of requiring more complexity to write.
There several roles that define various kinds of references in MyST.
In many cases, the short-hand syntax described above are aliases for these roles.
Below we describe the most commonly-used ones.

(ref-role)=

ref
: The {myst:role}ref role can be used to bring the title or caption directly in line, the role can take a single argument which is the label, for example, {ref}`reference-target`
: You can also choose the reference text directly (not taking from the title or caption) by using, {ref}`your text here <reference-target>`.

(numref-role)=

numref
: The {myst:role}numref role is exactly the same as the above {myst:role}ref role, but also allows you to use a %s in place of the number, which will get filled in when the content is rendered. For example, {numref}`Custom Table %s text <my-table-ref>`. will become Custom Table 3 text.

(eq-role)=

eq
: The {eq}`my-equation` syntax creates a numbered link to the equation, which is equivalent to [](#my-equation) as there is no text content to fill in a title or caption.

(doc-role)=

doc
: The {doc}`./my-file.md` syntax creates a link to the document, which is equivalent to [](./my-file.md).

(download-role)=

download
: The {download}`./my-file.zip` syntax creates a download to a document, which is equivalent to [](./my-file.zip).

:::{tip} When to use roles vs. short-hand

Short-hand syntax is provided for a quick and clean way to reference content with MyST if you are happy with default behavior.
However, some roles provide additional configurability that allow you to change their behavior.

Thus, a good rule of thumb is the following:

  1. If you're happy with the default behavior of references, use @target syntax.
  2. If you only wish to modify the displayed text, use [My text](#target) syntax.
  3. If you wish to use more complex configurability, use the role syntax ({ref}, {cite}, etc).

:::

Targets and labels for referencing

The content you wish to reference is called a target, and to be targeted that content must have a label.
For example, in this reference syntax:

md
[](#my-targets-label)

The text my-targets-label is the label for the target.
There are many ways that you can label a target, and the sections below describe the most common approaches.

:::{note}
To be referenced, targets must have a label/identifier pair in the AST.
:::

Directive Targets

Labels can be given to most directives by setting the label option. For example, to label and reference a figure, use the following syntax:

{myst}
```{figure} https://github.com/rowanc1/pics/blob/main/mountains.png?raw=true
:label: my-fig
:align: center

My **bold** mountain 🏔🚠.
```

Check out [](#my-fig)!!
{tip} using markdown links 🔗
You can use this syntax to also reference [Section/Header targets](#targeting-headers) as well as [label equations](#referencing-equations) when using [dollar math](#dollar-math) or [AMS math](#ams-environments).

Or, to label an equation:

(example-equation-targets)=

{myst}
```{math}
:label: my-math-label
e=mc^2
```

See [](#my-math-label) for an equation!

(targeting-headers)=

Header Targets

To add labels to a header use (my-section)= before the header, these can then be used in markdown links and {myst:role}ref roles. This is helpful if you want to quickly insert links to other parts of your book. Referencing a heading will show the heading and the subsequent two pieces of content[^heading-content], unless a header is encountered.

[^heading-content]: The content could be a single paragraph, a figure, table or list. It can also be fully interactive content, with cross-references to other content, allowing you to nest and follow references easily!

{myst}
(my-section)=
#### Header _Targets_

Use `(label)=` before the element that you want to target, then reference content with:

* [](#my-section)

:::{tip} How to turn on heading numbering
:class: dropdown

By default headings are not numbered, see for more information. To turn on numbered headers you need to turn numbering on in the document or project using numbering in the frontmatter. You can control this for each heading level:

yaml
numbering:
  heading_1: true
  heading_2: true

These will show up, for example, as Section 1 and Section 2.1. To turn on all heading numbering, use headings: true.
:::

% TODO: We should support pandoc style unnumbered {-} and {.class, #id} syntax

(header-numbering)=

Header Numbering

By default section numbering for headers is turned off with numbering for figure and table numbering enabled.
To turn on numbering for headers, you can can change the frontmatter in the document or project. See for more details on the numbering object.

{myst}
---
numbering:
  heading_2: true
  heading_3: true
---

(my-chapter)=
## My Chapter

(my-section)=
### My Section

(my-section2)=
### My Second Section

* [](#my-chapter)
* [](#my-section)
* [](#my-section2)

(targeting-cells)=

Notebook Cell Targets

You can label notebook cells using a comment at the top of the cell, using a #| label: syntax, or have this added directly in the notebook metadata for the cell.

python
#| label: my-cell
print('hello world')

The cell output or the entire cell can be embedded or referred to using the image or link syntax.

markdown
[](#my-cell) - This is a cross-reference to a notebook cell
 - This will embed the output of a notebook cell

or as a figure directive, where you can then add a caption. If you are referring to that figure in a further cross reference that figure (i.e. not the original cell), give it a new name.

markdown
:::{figure} #my-cell
:label: fig-my-cell
:::

:::{card} ♻️ See Also: Reusing Jupyter Outputs
:link: ./reuse-jupyter-outputs.md
See more about reusing Jupyter outputs in figures, adding placeholders, and other options.
:::

:::{note} Interactive Example

The following example embeds a figure from , and can be used in cross references .

{figure} #img:altair-horsepower
:label: fig-altair-horsepower
This figure has been included from [](./interactive-notebooks.ipynb) and can be referred to in cross-references through a different label.

:::

(label-anything)=

Label Anything

It is possible to label any document node by adding (my-label)= before any other block of content. These can be referenced using the {myst:role}ref role, but by default will not be enumerated, so you cannot use %s or {number} in the content.

{myst}
(my-paragraph)=
This is just a paragraph!

(my-points)=
* Bullet
* points

Please see [this paragraph](#my-paragraph) and [these points](#my-points).

(numbering)=

Numbering

Frontmatter may specify numbering to customize how various components of the page are numbered. By default, numbering is enabled for figures, equations, tables, math, and code blocks; it is disabled for headings and other content types contained on the page. Additionally, by default, numbering resets on every page.

Enabling and Disabling Numbering

To enable numbering of all content, you may simply use:

yaml
numbering: true

Similarly, to disable all numbering:

yaml
numbering: false

The numbering object allows you to be much more granular with enabling and disabling specific components:

yaml
numbering:
  code: false
  equations: false
  headings: true

Numbering may be used for figure as above, as well as subfigure, equation, subequation, table, code, headings (for all heading depths), and heading_1 through heading_6 (for modifying each depth separately).

Numbering Custom Content

You may also add numbering for custom content kinds:

markdown
---
numbering:
  box:
    enabled: true
---

:::{figure} image.png
:label: my-box
:kind: box

This figure will be numbered as "Box 1"
:::

Customizing Numbering Appearance

By default, all components are numbered sequentially as 1, 2, 3... However, under the numbering object, you may specify enumerator. For now, this applies to all numberings on the page. Instead of enumerating as simply 1, 2, 3... they will follow the template set in enumerator. For example, in Appendix 1, you may want to use the following numbering so content is enumerated as A1.1, A1.2, A1.3...

yaml
numbering:
  enumerator: A1.%s

If you want to control the numbering for a specific figure, you can use the {myst:directive}figure.enumerator option. This will give the figure a specific enumerator, and will not increment the counting for other figures. This is helpful if you want to explicitly count figure 2a and then carry on counting figures as normal; alternatively you can take control of numbering entirely by setting {myst:directive}figure.enumerator on every figure.

You may also redefine how the component will render when referenced in the text by specifying template. For this example, the figures on the page will start with Figure 1 and when referenced in the text they will appear as "fig (1)"

yaml
numbering:
  figure:
    template: fig (%s)

Continuous Numbering

By default, numbering will reset on every page. However, you may enable continuous numbering across all pages by specifying continue: true in your numbering object. If this is specified on a single page, it will continue counting from the previous page; if specified in your myst.yml, counting will be continuous across the entire project.

yaml
numbering:
  figure:
    continue: true

You may also override the start value to begin counting on the page at a specific number. This is useful, for example, if you want continuous figure numbering across your project until you reach your appendix, then you want to reset to A1:

yaml
numbering:
  enumerator: A%s
  figure:
    start: 1

Title Numbering

By default, page titles are not numbered, even if you turn on all numbering with numbering: true. However, you may add title to your numbering object:

yaml
numbering:
  title: true

Doing so will enumerate all the titles in your MyST project. These numberings will follow the structure of your table of contents. So, given the structure:

yaml
toc:
  - file: index.md
  - file: section_a.md
    children:
      - file: chapter_1.md
      - file: chapter_2.md
  - file: section_b.md

The numbering will be: 1 - index.md, 2 - section_a.md, 2.1 - chapter_1.md, 2.2 - chapter_2.md, and 3 - section_b.md.

If both title and heading numbering are enabled, these will be incremented together. So, for the example above, if there is a single heading of level 1 in section_a.md, it will increment the same count as the title of chapter_1.md and chapter_2.md (i.e. the heading will be 2.1 and the chapters will be 2.2 and 2.3):

yaml
numbering:
  title: true
  headings: true

You can further customize this behavior by setting an explicit offset value on a page. By default the offset matches the nesting level in the table of contents, but, for example, if you want page numbering to be flat, despite nesting in the table of contents, you can set offset: 0:

yaml
numbering:
  title:
    enabled: true
    offset: 0

For the example above, the numbering will change to: 1 - index.md, 2 - section_a.md, 3 - chapter_1.md, 4 - chapter_2.md, and 5 - section_b.md.


Deployment Curvenote (`docs/deployment-curvenote.md`)


title: Deploying to Curvenote short_title: Curvenote description: Deploy your MyST site to Curvenote

Curvenote provides a free service to host your MyST sites with an up-to-date theme (deployment documentation for MyST sites). The websites are hosted on a curve.space subdomain with your username or a custom domain. To configure the domain(s) add them to your site configuration:

yaml
site:
  domains:
    - username.curve.space

You can then deploy the site using:

bash
curvenote deploy

You can also deploy from a GitHub action, which will build your site and then deploy it to Curvenote.

🛠 In the root of your git repository run myst init --gh-curvenote

The command will ask you questions about which branch to deploy from (e.g. main) and the name of the GitHub Action (e.g. deploy.yml). It will then create a GitHub Action[^actions] that will run next time you push your code to the main branch you specified. Ensure that you including setting up your CURVENOTE_TOKEN which can be created from your Curvenote profile.

:::{tip} Full GitHub Actions
:class: dropdown

You can use GitHub actions to build and deploy your site automatically when you merge new documents, for example.

See the documentation for the action, including setting up your CURVENOTE_TOKEN:
https://github.com/curvenote/action-myst-publish

yaml
name: deploy-myst-site
on:
  push:
    branches:
      - main
permissions:
  contents: read
  pull-requests: write
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy 🚀
        uses: curvenote/action-myst-publish@v1
        env:
          CURVENOTE_TOKEN: ${{ secrets.CURVENOTE_TOKEN }}

:::

[^actions]: To learn more about GitHub Actions, see the GitHub documentation. These are YAML files created in the .github/workflows folder in the root of your repository.


Deployment Github Pages (`docs/deployment-github-pages.md`)


title: Deploy to GitHub Pages short_title: Github Pages description: Deploy your MyST site to GitHub pages.

GitHub Pages allows you to host static HTML files online from GitHub repositories using GitHub Actions.
This page has important information for how to do so.

Instructions

To get setup with GitHub Pages, ensure that your repository is hosted in GitHub and you are in the root of the Git repository.
There's a special init function which adds the proper configuration for deploying to GitHub Pages with a GitHub Action.

🛠 In the root of your git repository run myst init --gh-pages

The command will ask you questions about which branch to deploy from (e.g. main) and the name of the GitHub Action (e.g. deploy.yml). It will then create a GitHub Action[^actions] that will run next time you push your code to the main branch you specified.

:::{figure} ./images/myst-init-gh-pages.png
:class: framed
The command myst init --gh-pages will guide you through deploying to GitHub Pages.
:::

[^actions]: To learn more about GitHub Actions, see the GitHub documentation. These are YAML files created in the .github/workflows folder in the root of your repository.

🛠 In your repository, navigate to {kbd}Settings -> {kbd}Pages and enable GitHub Pages by choosing {kbd}GitHub Actions as the source.

This has activated GitHub Pages to accept new HTML from GitHub actions.

To trigger the action, push new commits of code to the branch that you've configured with the action above. You should start seeing your website show up at <githuborg>.github.io/<githubrepo>.

`BASE_URL` Configuration for GitHub Pages

The MyST CLI needs to know the destination (base URL) of your site during build time. If you setup deployment to GitHub Pages using the myst init --gh-pages command, then this is handled automatically for you. Otherwise, if you deploy your website from a repository that's not the default GitHub Pages repository (i.e., not called <username>.github.io), you likely need to define a BASE_URL that includes the repository name.[^except-custom-domains]
[^except-custom-domains]: If you're using a custom domain, you may not need to set BASE_URL if the site is served from the base domain (e.g.: mydomain.org) without a sub-folder (e.g., mydomain.org/mydocs/).

Example: A Full GitHub Action

The GitHub Action below builds and deploys your site automatically.
Click the dropdown to show it, and copy/paste/modify as you like.

:::{note} GitHub Action Example
:class: dropdown

{code} yaml
:filename: deploy.yml
# This file was created automatically with `myst init --gh-pages` 🪄 💚

name: MyST GitHub Pages Deploy
on:
  push:
    # Runs on pushes targeting the default branch
    branches: [main]
env:
  # `BASE_URL` determines the website is served from, including CSS & JS assets
  # You may need to change this to `BASE_URL: ''`
  BASE_URL: /${{ github.event.repository.name }}

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
  contents: read
  pages: write
  id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
  group: 'pages'
  cancel-in-progress: false
jobs:
  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup Pages
        uses: actions/configure-pages@v3
      - uses: actions/setup-node@v4
        with:
          node-version: 24.x
      - name: Install MyST Markdown
        run: npm install mystmd
      - name: Build HTML Assets
        run: myst build --html
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: './_build/html'
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

:::


Deployment Gitlab (`docs/deployment-gitlab.md`)


title: Deploy to GitLab Pages short_title: GitLab Pages description: Deploy your MyST site to GitLab pages.

GitLab Pages allows you to host static HTML files online from GitLab repositories using GitLab CI/CD.
This page has important information for how to do so.

Instructions

:::{warning}
Because of your institution’s GitLab configuration, the descriptions below may differ from the actual deployment on GitLab Pages.

If you are new to GitLab or you are not familiar with git, please proceed to
:::

To get setup with GitLab Pages, ensure that your repository is hosted in GitLab and you are in the root of the Git repository.

Deployment with uv

Create .gitlab-ci.yml file in the root of your project with the content provided below:

{code} yaml
:filename: .gitlab-ci.yml
:linenos:
:emphasize-lines: 47

image: ghcr.io/astral-sh/uv:debian-slim

stages:
  - build
  - deploy

variables:
  HOST: "127.0.0.1"

cache:
  paths:
    - .venv

before_script:
  - uv --version

build:
  stage: build
  script:
    # initialize uv project and install jupyter-book
    - uv init
    - uv add "jupyter-book>=2.1.2,<3"

    # install node
    - apt-get update
    - apt-get install -y curl
    - apt-get install -y procps
    - curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
    - apt-get install -y nodejs

    # run jupyter-book via uv
    - uv run jupyter-book clean --all -y
    - export BASE_URL=""
    - uv run jupyter-book build --html
  artifacts:
    paths:
      - _build/html

pages:
  stage: deploy
  script:
    - mv _build/html/ public
  artifacts:
    paths:
      - public
  only:
    - main # replace it with YOUR branch name

then push this file to your GitLab repo.

If your Git branch is different from main, you should replace main in the .gitlab-ci.yml file on the highlighted line with your branch name.

Once everything done, you should see GitLab Pages link in the right menu as shown below:

{figure} ./images/gitlab-pages.png
:height:300px

Deployment with poetry

Create a file called .gitlab-ci.yml with the following content:

text
/* Detailed source-code truncated for AI context efficiency. */

Deployment with pixi

Note: You may not use pixi in your project but you may use it for GitLab deployment.

  1. Make sure that you have pixi installed on your machine.

  2. Create a file called .gitlab-ci.yml with the following content:

{code} yaml
:filename: .gitlab-ci.yml
:linenos:
:emphasize-lines: 39 

image: ghcr.io/prefix-dev/pixi:latest

stages:
  - build
  - deploy

variables:
  PIXI_CACHE_DIR: "$CI_PROJECT_DIR/.pixi"
  HOST: "127.0.0.1"

cache:
  paths:
    - .pixi

before_script:
  - pixi --version

build:
  stage: build
  script:
    # install environment from pixi.toml + pixi.lock
    - pixi install --locked

    # run jupyter-book via pixi environment
    - pixi run jupyter-book build --html
  artifacts:
    paths:
      - _build/html

pages:
  stage: deploy
  script:
    - mkdir public
    - cp -r _build/html/* public/
  artifacts:
    paths:
      - public
  only:
    - main # replace it with your branch name!

If your Git branch is different from main, you should replace main in the .gitlab-ci.yml file on the highlighted line with your branch name.

  1. Make sure your pixi project is initialized (you may use pixi init CLI command to do so)

  2. Make sure that your pixi.toml file contains linux-64 as a platform and python with jupyter-book as dependencies.
    A minimal version of the pixi.toml file is shown below.

{code-block} toml
:filename: pixi.toml

[workspace]
authors = ["Me <[email protected]>"]
channels = ["conda-forge"]
name = "jbtest"
platforms = ["win-64", "linux-64"]
version = "0.1.0"

[tasks]

[dependencies]
python = ">=3.14.3,<3.15"
jupyter-book = ">=2.1.2,<3"
  1. Synchronize pixi.lock file using pixi lock CLI command.

Note that a pixi.toml and pixi.lock file should be tracked by Git and pushed to the repository with other files.

  1. Add your project files to git using git add (e.g git add myst.yml main.md pixi.toml pixi.lock)
  2. Commit the changes
  3. Push your branch to GitLab (e.g git push -u origin main)

External server through GitLab CI/CD

Another option is to deploy your MyST site to an external server through GitLab CI/CD. A main difference is setting 'variables' (settings $\rightarrow$ CI/CD $\rightarrow$ variables) for connecting to the server.

An example .gitlab-ci.yml file for this deployment method is shown below.

{code} yaml
:filename: .gitlab-ci.yml
:lineos:

stages:
  - deploy

image: python:3.11-slim

variables:
  SSH_COMMAND: 'ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes'
  LOCAL_BUILD_DIR: "_build/html"
  HOST: "127.0.0.1"
  BASE_URL: ""

before_script:
  - apt-get update
  - apt-get install -y --no-install-recommends curl rsync openssh-client git procps

  # Node.js
  - curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
  - apt-get install -y --no-install-recommends nodejs
  - node --version
  - npm --version

  # Python deps
  - python -m pip install --upgrade pip
  - pip install mystmd

  # Load SSH key
  - eval "$(ssh-agent -s)"
  - chmod 400 "$WEBSITE_UPLOAD_KEY"
  - ssh-add "$WEBSITE_UPLOAD_KEY"

deploy:
  stage: deploy
  script:
    # Build (important: define the project path)
    - myst build --html

    - rsync -ravz "${LOCAL_BUILD_DIR}/" -e "${SSH_COMMAND} -i ${WEBSITE_UPLOAD_KEY}" "${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}/"

Note that this way of deploying requires a gitlab runner.

Instructions for beginners

If this is your first time using GitLab, complete these one-time setup steps:

  1. Install Git
  2. Configure your git by using the following commands in the terminal
    shell
    git config --global user.name "Your Name"
    git config --global user.email "[email protected]"
  3. Create a GitLab account on official
    GitLab
    or on the GitLab of your
    institution.
  4. Set up SSH authentication

Once set up, you have two options to connect your project to GitLab:

  1. Start from GitLab: Create a repository on GitLab, clone it to your computer, add your files, and push them
  2. Start from your computer: Initialize your local folder as a Git repository and push it to GitLab using the following commands:
    bash
    cd your-project-folder   # go to your local folder with your myst project
    git init                 # initialize git
    git add .                # stage all files
    git commit -m "first commit"
    git remote add origin [email protected]:username/project.git # you may change this link with the link of your repository
    git push -u origin main

Once your git project is initialized, create .gitlab-ci.yml file in the root
of your myst project and paste the contents described in to the file.

Once you saved the file with provided contents, execute the next commands:

shell
git add .gitlab-ci.yml
git commit -m "added .gitlab-ci.yml to set up GitLab Pages"
git push

You are all done. You may proceed to the page of your GitLab repository and
open GitLab Pages.


Deployment Netlify (`docs/deployment-netlify.md`)


title: Deploying to Netlify short_title: Netlify description: Deploy your MyST site to Netlify

% Adapted from: https://jupyterbook.org/en/stable/publish/netlify.html#step-2-add-the-command-to-install-and-build-your-book

Netlify is a continuous deployment service that can
automatically build an updated copy of your project as you push new content.
It can be used across git clients including GitHub, GitLab, and Bitbucket.
Although Netlify has both free and paid tiers, the build process is the same across both.
Importantly, the free tier only allows for 100GB of bandwidth usage per month across all of your Netlify built projects[^pricing].

:::{note} You may wish to set more permissive CORS settings
To allow other MyST sites to reference and use your content, ensure that Netlify has the proper CORS configuration. For an example, see this GitHub PR updating CORS settings for Netlify. See for more information.
:::

[^pricing]: If your project will be used by a large audience, or if you're creating many projects, you might want to consider registering for a paid account.

These instructions assume you're keeping your source files under version control,
rather than the built project HTML.

In order to use Netlify, you'll need to create an account.
Here, we'll walk through connecting your project to Netlify's continuous deployment services using their UI.
You can also check out their documentation on continuous deployment.

Step 1: Connect your GitHub repo to Netlify

After you've created a Netlify account, you'll need to log in.
The home page will be a dashboard of all sites you're currently building with Netlify.
We can then import a new site by clicking the "New Site from Git" button in the upper right.

This should launch the site builder:

Here, you can select the git client where your project is hosted.
For the purposes of this tutorial, we'll assume that your book is hosted on GitHub.

When you select the "GitHub" option, you'll be asked to grant permission for Netlify to access your GitHub account.
Authorizing access will take you to the next step of the build process, where you can select your project repository.

Step 2: Add the command to install and build your project

:::{note}
If your book content is not in the root of your repository, make sure you point to
where you will run the myst build --html command locally.
:::

Once you've selected the correct repository, you'll need to supply build instructions.
This is a command that Netlify runs before hosting your site. We'll use it to do the
following:

  • Install mystmd
  • Build your project's HTML

Put the following command in the Build command section:

bash
npm install mystmd && myst build --html

Finally, the Publish directory should be _build/html.

You'll also need to select the appropriate branch to build your repository from.
In this example, we'll use the main branch.

You can then select Deploy site and wait for the site to build.
You'll be redirected to the site dashboard during the build process.

Step 3: Updating your domain name

If your site has successfully built, it will be assigned a random domain name.
In order to have a more memorable address, you can update your site's name.

From the site dashboard, select Domain settings.
This will take you to a sub-menu, where you can choose to update your site name.

You can enter a memorable, unique name here to describe your project!
Note that it will be prepended to .netlify.com so, MY-BOOK will become MY-BOOK.netlify.com.

You can also use a custom domain (i.e., one that you have purchased through a DNS registrar).
See the Netlify documentation on custom domains for more details on this process.


Deployment Readthedocs (`docs/deployment-readthedocs.md`)


title: Deploy to Read the Docs short_title: ReadTheDocs description: Deploy your MyST site to Read the Docs.

Read the Docs is a popular platform for hosting documentation that provides free hosting for open source projects and supports automatic builds from your Git repository.
This page has important information for how to deploy MyST sites to Read the Docs.

Instructions

To get setup with Read the Docs, ensure that your repository is hosted on GitHub, GitLab, or Bitbucket, and you are in the root of the Git repository.
There's a special init function which adds the proper configuration for deploying to Read the Docs.

🛠 In the root of your git repository run myst init --readthedocs

The command will ask you which Python and Node.js versions to use. It will then create a .readthedocs.yaml configuration file in the root of your repository.[^python-note]

[^python-note]: While MyST builds only require Node.js, the configuration also includes Python. This is useful if you need to execute code in Jupyter notebooks or if you're using Jupyter Book features alongside MyST.

After creating the configuration file, you'll need to:

🛠 Create an account on readthedocs.org if you don't have one

🛠 Import your project from your Git hosting provider (GitHub, GitLab, or Bitbucket)

🛠 Commit and push the .readthedocs.yaml file to your repository

Read the Docs will automatically build your documentation on each push. Your documentation will be available at https://YOUR-PROJECT.readthedocs.io.

ReadTheDocs JavaScript might interfere with your site

By default, ReadTheDocs enables documentation addons that execute JavaScript on the page.
This may interfere with the React-based rendering that the myst-theme uses.
You might see an occasional "flicker" of the content as a result.
Experiment with disabling these add-ons and it may improve your outcomes, your mileage may vary!

Example: A Full Read the Docs Configuration

The configuration file below builds and deploys your site automatically.
Click the dropdown to show it, and copy/paste/modify as you like.

:::{note} Read the Docs Configuration Example
:class: dropdown

{code} yaml
:filename: .readthedocs.yaml
# Read the Docs configuration file for myst
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# This file was created automatically with `myst init --readthedocs`

# Required
version: 2

# Set the OS, Python version, and Node.js version
# Note: Python is included for executing code in notebooks or using Jupyter Book
build:
  os: ubuntu-22.04
  tools:
    python: "3.12"
    nodejs: "22"
  commands:
    # Install mystmd
    - npm install mystmd
    # Build the site
    - myst build --html
    # Copy the output to Read the Docs expected location
    - mkdir -p $READTHEDOCS_OUTPUT/html/
    - cp -r _build/html/. "$READTHEDOCS_OUTPUT/html"
    # Clean up build artifacts
    - rm -rf _build

:::


Deployment Webserver (`docs/deployment-webserver.md`)


title: Deploy on an Apache web server (httpd) short_title: Apache httpd description: Deploy your MyST site on Apache httpd

Once you have built a static version of your MyST project via myst build --html, it can be served on an any common web server: Apache web server (httpd), nginx, or others.

For example, let's assume your web server's base URL is http://www.example.com and it uses /var/www/html as the file server root (DocumentRoot in httpd). If you copy the _build/html/ directory to /var/www/html/my-project using

shell
cp -r _build/html /var/www/html/my-project

then the resulting URL would be http://www.example.com/my-project, and you are all set if you have configured the base URL.


Deployment (`docs/deployment.md`)


title: Host your MyST Site short_title: Web hosting description: Deploy your MyST site to static HTML or a MyST-aware server.

There are two different ways to host MyST websites online.

  • As a static website. An HTML file is generated for each page at once. The collection of HTML files and website assets can be served from static site hosts (like GitHub Pages, Netlify, and ReadTheDocs).
  • As an application. A MyST server is run that dynamically generates pages when a user visits them. Your site can be served by a hosting provider that supports applications (like Curvenote or Vercel)

The high-level differences between these approaches are outlined in .
:::{table} High-Level Comparison of MyST Site Deployment Types
:label: deployment-comparison

Deployment Type High Performance Seamless Updates Static Hosting
Static website ❌ ❌ ✅
MyST server ✅ ✅ ❌

:::

:::{note} MyST was designed to be deployed as an application
Deploying MyST as an application has many benefits. For example, performance enhancements (like pre-fetching for instant page-transitions, loading indicators, and smaller network payloads) and easier upgrades as new MyST versions are released.

The default themes for MyST are designed to be MyST applications rather than static sites, but the core functionality is equally shared between the two options.
:::

% - Static deployments are MPA (each page own HTML document), SSG (rendered ahead of time)
% - Servers are SPA (request JSON), SSR+hydration
%- Term definitions?
% - SPA vs MPA
% - SSR vs CSR vs SSG

The choice between static hosts and application-based hosts often comes down to the type of website hosting that is available, and the amount of complexity that you're willing to deal with in deploying your site.
The sections below share a few considerations to help you make a decision.

Static Websites

Creating Static HTML

To create a static HTML export of your MyST site, build it as HTML:

bash
myst build --html

After the build process, you can see the folder in _build/html, which has all assets for your static website. You can verify that the site is working correctly by starting a static web-server, for example,

shell
npx serve _build/html

which will serve a static version of the site.

:::{danger} Static sites should have an index.html
:class: dropdown

A static site needs an index.html at its root.
MyST generates one automatically when your site has a single project built at the root URL, which is the default.

If a root index.html is not generated, your project is being built under a nested URL instead of at the root.
You're probably using the deprecated projects: configuration, and should remove it.

:::

:::{tip} Ignore the _build folder in Git
:class: dropdown

If you are using Git, add the _build folder to your .gitignore so that it is not tracked. This folder contains auto-generated assets that can easily be re-built -- for example in a Continuous Integration system like GitHub Actions.
:::

:::{tip} Static HTML generates folder-based HTML files
:class: dropdown

When you generate static HTML using MyST, routes to documents (e.g. /folder/mydoc) must be rendered into static HTML files. MyST will turn routes into HTML paths like so:

text
/myfolder/mydoc/index.html

When the browser sends a request to the route /folder/mydoc, the server interprets that as request for the HTML file /myfolder/mydoc/index.html, and returns that.

Some SSGs instead return HTML files like /myfolder/mydoc.html, and this can trigger different behavior depending on the hosting platform you're using. However, /myfolder/mydoc/index.html is generally a safer bet, which is why it is the default.

See the trailing slash and hosting provider guide for more information about the impact of this behavior across hosting providers.

Inspired from the excellent Docusaurus documentation
:::

(deploy:base-url)=

Custom domains and the base URL

When deploying static HTML to the web, you may have to specify a Base URL so that links resolve properly.
This happens when you serve your MyST site from a subfolder of a parent URL.
For example:

  • mysite.org/: Does not require a BASE_URL to be set, because the root site is where your MyST site lives.
  • mysite.org/docs/: Does require a BASE_URL to be set, because your MyST HTML files will be in docs/, not the root.

If MyST detects an environment variable called BASE_URL it will prepend it to all links.

In the following examples we first define a BASE_URL parameter and then build the MyST HTML assets.

::::{tab-set}
:::{tab-item} Bash

bash
export BASE_URL="/repository_name"
myst build --html

:::
:::{tab-item} Powershell

powershell
$env:BASE_URL = "/folder1/folder2" 
myst build --html

:::
:::{tab-item} Fish

fish
set -x BASE_URL "/folder1/folder2"
myst build --html

:::
:::{tab-item} CMD

cmd
set BASE_URL=/folder1/folder2
myst build --html

:::
::::

:::{tip} Set BASE_URL in your CI/CD
You can set environment variables in many CI/CD services like GitHub Actions and Netlify.
This can be a useful way to ensure the BASE_URL is set before your site is deployed.
:::

Services for Static Web Hosting

The contents of the _build/html can be served from any static web server. The following articles outline the process of deploying your static site to well-known website providers:

(static-web-services-dropdown)=

:::{toc}
:context: children
:::

Application Websites

Creating Structured Site Data

MyST servers consume structured data that represents a MyST website. These data can be built using the myst build command,

shell
myst build --site

which populates the _build/site directory with information about your website including the metadata, cross-references, static images, and MyST AST.

Serving on a Network Interface

myst start only listens on localhost, so the site is not reachable from other machines.
To serve it to your network (for example, from a container or a remote server), set the HOST environment variable and pass --keep-host:

shell
HOST=0.0.0.0 myst start --keep-host

Without --keep-host, MyST resets HOST to localhost and prints a warning.

Deploying to a MyST-Aware Server

:::{note}
At present, only Curvenote provides out-of-the-box support for deployment of MyST websites:
:::

% TODO: vercel/netlify server

:::{card} Curvenote
:link: ./deployment-curvenote.md
Deploy to Curvenote as a dynamic site with a managed MyST theme.
:::


Developer Api (`docs/developer-api.md`)


title: MyST engine internals short_title: Engine internals description: How a build moves through the MyST engine, and which objects carry state between the stages.

This page explains how a build moves through the engine and which objects carry state between the stages.
It is for people changing the engine or calling myst-cli from their own code.
If you are writing a plugin, start with instead.
Each section links to the source on main, which may be newer than the version you have installed.

(api:pipeline)=

Build pipeline

{mermaid}
flowchart TD
    src["Source files(.md / .ipynb / myst.yml)"]
    out["Render(site JSON · PDF · JATS · DOCX)"]
    p1["🔌 DirectiveSpec · RoleSpec"]
    p2["🔌 TransformSpec stage: 'document'"]
    p3["🔌 LinkTransformerTransformSpec stage: 'project'"]

    subgraph per_file ["Per file"]
        parse["Parse → MDAST(PreRendererData)"]
        exec["Execute notebooks(myst-execute)"]
        dtrans["Document transforms→ RendererData"]
        parse --> exec --> dtrans
    end

    subgraph project ["Project"]
        ptrans["postProcessMdast(resolve links and cross-references,then project transforms)"]
    end

    src --> per_file
    p1 -.-> parse
    p2 -.-> dtrans
    per_file --> project
    p3 -.-> ptrans
    project --> out

Directives and roles run inside the parser, so they see no other file and no execution output.
Document transforms then run once per file in transformMdast, which ends by building that file's RendererData.
Once every file is loaded, MyST collects cross-reference targets from all of them.
Finally postProcessMdast, in the same module, runs once per file.
It resolves links and cross-references in place and then runs the project transforms.
See for what the AST looks like before and after.

Every engine function in these stages takes an ISession.
Plugins never receive a session, as explained in .

(api:session)=

Session

Every stage shares one session object.
It carries the logger, the HTTP client, the Redux store, the build paths, and the loaded plugins.
Two types share the name.

(api:session-cache)=

Session cache: `ISessionWithCache`

At runtime the session also caches per-file data such as parsed trees, citation renderers, and execution outputs.
ISessionWithCache is the wider type that exposes these caches.
Call castSession(session) to get the wider type.
The two types are separate so that code which only needs ISession does not have to import every myst-* package that the cache fields depend on.
Both types and castSession are exported from myst-cli, but neither is a stable public API.

  • $mdast maps each file to its pre and post data (see ). Read and write entries through $getMdast(file) and $setMdast(file, data).
  • The other caches are $citationRenderers (keyed by file), $doiRenderers (keyed by DOI), $externalReferences, $outputs, and $siteTemplate.

Plugin transforms never see the session.
If you drive the build yourself, you can pass extraTransforms to transformMdast or processProject instead.
MyST calls each transform as transform(session, opts) once per file, right after that file's RendererData is stored.
The opts argument is the same options object that transformMdast received.

(api:document-data)=

Document data

  • A VFile wraps one document while transforms run. Its path is the absolute source path. Messages attached to the file become build warnings, as described in .
  • PreRendererData is created when a file is loaded. It holds the file path, its location in the project, the parsed mdast, the kind (Article, Notebook, or Part), and optional frontmatter, identifiers, and widgets.
  • RendererData extends PreRendererData with sha256, slug, the resolved frontmatter, references, and dependencies. transformMdast builds the RendererData, postProcessMdast updates the object in place, and the site content JSON is the serialised form.

(api:ast)=

AST node types

{code} typescript
import { visit } from 'unist-util-visit';
import { selectAll } from 'unist-util-select';

visit(tree, 'code', (node) => {
  if (!node.lang) node.lang = 'text';
});
const figures = selectAll('container[kind=figure]', tree);

(api:link-transformer)=

DOIs, arXiv IDs, and internal cross-references each need their own handling.
Link resolution is therefore a chain of LinkTransformer objects, tried in order during post-processing.

  • test(uri) returns whether this transformer handles the URI.
  • transform(link, vfile) mutates the Link node in place and returns true on success.
  • protocol names the scheme. Set formatsText to true if your transformer sets the link text itself, so that MyST leaves it alone.

The built-in transformers live in myst-transforms/src/links/.
To add your own, pass them as extraLinkTransformers to postProcessMdast or processProject.
They run before the built-ins.
There is no way to add them from myst.yml.

(api:config)=

Where the other types live

  • ProjectConfig and SiteConfig are the validated forms of myst.yml. Read them from the Redux store with the selectors exported from myst-cli.
  • ICache and LocalDiskCache in myst-execute cache cell outputs on disk, one JSON file per key, so that rebuilds skip unchanged cells.
  • Logger is Pick<typeof console, 'debug' | 'info' | 'warn' | 'error'>. Use session.log for build-level messages and fileWarn from myst-common for anything tied to a document, as described in .

Developer (`docs/developer.md`)


title: Developer Guide short_title: Developer Guide

These sections help you get started with a development environment for mystmd and learn how to contribute to the codebase. Note that mystmd is written in TypeScript.

We start by discussing the project architecture, and give a birds eye view of how the various parts fit together.
Then, we delve into the specific tools you need, and the developer workflow for each part.
Finally, we go over requirements for contributing code back, via GitHub Pull Requests.

(architecture)=

Architecture underlying a MyST build

From an author's perspective, mystmd can be thought of as a tool which compiles files in MyST Markdown format,
a variant of Markdown with several extensions, into books, articles, and websites.
Like other such tools, it can produce static output, such as PDF, docx, or HTML.
See for a conceptual overview.

However, for a developer it is important to understand that mystmd consists of two parts:

  1. An engine that converts input documents into an AST (abstract syntax tree).
  2. A renderer, that converts that AST into a given output format.

:::{seealso} Learn more about the AST
If the AST is unfamiliar, start with the AST primer for a short introduction to its structure.
:::

This model is equally applicable to exporting/converting static documents and rich web applications. Here's a workflow for static documents:

{mermaid}
flowchart LR
  subgraph "<tt>myst build</tt>"
  myfile.md --> AST --> PDF
  end

Here's a workflow for producing a rich web application:

(diagram-app)=

{mermaid}
flowchart TB
  subgraph "myst start --headless"
    subgraph "**Document conversion**"
    direction LR
    doc[folder_of/myfile.md] --> ast[AST]
    end
    subgraph "**Content server**"
    ast --> content_server[serve AST on http://localhost:3100]
    end
  end
  subgraph "bun run theme:book"
    subgraph "**Theme server**"
    direction LR
    theme_server[render themed content at http://localhost:3000] --> content_server
    user[User browser] --> theme_server
    end
  end

Walking through this last example, we see that invoking myst start --headless converts a set of input files into an AST, and serves the resulting AST via a content server.
The theme server is a React app that knows how to style data, which it pulls in from the content server.
The user interacts with the React app, which may trigger new fetches from the content server.
For what happens inside the engine at each stage of the build, see .

Project-specific concepts

mystmd is built on top of well known tools in the JavaScript ecosystem, such as unist from unified, mdast, and citation.js for the myst CLI. The default MyST web themes use React, Remix (potentially vite soon), and Tailwind CSS for the theme server.

If you are familiar with these tools, you should not find many surprises in the codebase.

That said, there are a couple of concepts used only in this project, that won't be familiar. These are detailed below:

(develop-renderers-themes)=

Concepts: Renderers, themes, and templates

In the diagram above, we saw that mystmd produces websites by:

  • Parsing a set of documents to an AST.
  • Transforming the AST into a resolved AST.
  • Rendering the AST into components that can be used by a template or theme.
  • Exporting into a final output by a template / theme.

This section describes a bit more how Rendering and Exporting work using Themes and Templates.

For an introduction to themes and templates, see . For a hands-on guide to building a web theme, read . In addition, below we'll define what a Renderer is:

{glossary}
Renderer
: Converts MyST AST into components that {term}`themes` and {term}`templates` can use to export final outputs. For example, the [`myst-to-react` renderer](https://github.com/jupyter-book/myst-theme/tree/main/packages/myst-to-react) converts MyST AST into a number of React components that the [`book` and `article` React themes](https://github.com/jupyter-book/myst-theme/tree/main/themes) use to generate websites.

For example, in the case of the book theme, a MyST Document engine serves MyST AST via a Content Server, the MyST React renderer ingests that content and output React components, and the book theme converts those components into HTML outputs.

Where to find renderers, themes, and templates

MyST has multiple renderers, themes, and templates that allow it to transform MyST AST into final output formats. The MyST team maintains a few that are worth noting:

:::{error} to do — explain rendering

  • describe the render loop, and how render blocks are registered
  • explain the ThemeProvider
  • explain styling

:::

Example: Adding an "edit this page" button

Here's a brief example to illustrate a common development pattern.
Let's say we want to add a new button to each page of the MyST Theme that includes an "edit link" for the page.

To accomplish this, we need to make three contributions.

First, update jupyter-book/mystmd, so that we can expose the "edit link" for each page as a new piece of metadata. The main logic for adding the edit URL is here.

Second, update the React theme infrastructure at jupyter-book/myst-theme. Here's a pull request that implements this. Note how it:

  1. Defines a new React component called <EditLink />.
  2. Pulls the new edit URL metadata from the MyST AST.
  3. Adds the new <EditLink /> component to the <FrontMatterBlock /> component.
  4. Because the book theme and the article theme already use the <FrontMatterBlock />, they inherit this functionality automatically.

(develop:transforms)=

Concepts: MyST Transformers

See for a higher-level overview of transforms.
This section provides a few concrete examples of how transforms modify the AST throughout the transforms process.

Example: Parsing an admonition

Let's say you've got MyST Markdown that defines an admonition, like this:

{code} markdown
:filename: page1.md
:::{note} Here's an admonition
:::

The result of Parsing phase will be raw MyST AST (normally, this is .json but we'll show it in YAML so it is a bit more readable):

{code} yaml
:filename: page1.json
- type: mystDirective
  name: note
  args: Here's an admonition
  children:
    - type: admonition
      kind: note
      children:
        - type: paragraph
          children:
            - type: text
              value: Here's an admonition

The AST simply encodes that there's a Directive present, with the name note.

After initial parsing, all Directive nodes are run, triggering the Admonition Directive logic. This converts the Directive Node into an Admonition Node.

During the transform phase, the Admonition Transforms is applied to each Admonition node. These do things like double-check that the admonition has all the necessary information to be rendered.

The final output has more admonition-specific metadata defined, like admonitionTitle.

{code} yaml
:filename: page1.json
- type: admonition
  kind: note
  children:
    - type: admonitionTitle
      children:
        - type: text
          value: Note
    - type: paragraph
      children:
        - type: text
          value: Here's an admonition

Example: Parsing a cross-reference

Let's say you have a page that labels some content, and cross-references it elsewhere on the page:

{code} markdown
:filename: page1.md
(label)=
**A labeled paragraph**.

A reference to [my label](#label)

The initial parse of this page creates nodes where labels are present, and treats our markdown link syntax as a regular URL.

{code} yaml
:filename: page1.json
- type: mystTarget
  label: label
- type: paragraph
  children:
    - type: strong
      children:
        - type: text
          value: A labeled paragraph
- type: paragraph
  children:
    - type: text
      value: 'A reference to '
    - type: link
      url: '#label'
      children:
        - type: text
          value: my label

During the Transformations phase, a number of enumeration transforms are run, including one that detects and resolves cross-reference links. At the end of these transforms, the AST now correctly encodes that we have a cross reference rather than a "normal" URL link. This can now be rendered into various output formats.

{code} yaml
:filename: page1.json
- type: paragraph
  children:
    - type: strong
      children:
        - type: text
          value: A labeled paragraph
  label: label
  identifier: label
  html_id: label
- type: paragraph
  children:
    - type: text
      value: 'A reference to '
    - type: crossReference
      children:
        - type: text
          value: my label
      urlSource: '#label'
      identifier: label
      label: label
      kind: paragraph
      template: Paragraph
      resolved: true
      html_id: label

Concepts: Frontmatter inheritance

Some frontmatter fields inherit from project-level config to individual pages.
This is controlled by the USE_PROJECT_FALLBACK list.
To make a new field inherit from project to page, add its key to this list.

Tools used in development

mystmd is built and developed using:

  • Bun, which provides a JavaScript runtime and package management;
  • TypeScript for static type checking;
  • ESLint for code linting;
  • Prettier for code formatting; and
  • Changesets for versioning and changelogs.
{note}
Below you will see several `bun run x` commands.
These are simply aliases for other commands, defined in the [`package.json` file](https://github.com/jupyter-book/mystmd/blob/main/package.json) under "scripts".

(developer:mystmd)=

Local development: `mystmd`

The mystmd libraries and command line tools are written in TypeScript, and require Bun for local development.

:::{warning} Don't use mystmd-py and the NPM installation at the same time
:class: dropdown

The mystmd-py package is a thin Python wrapper around the mystmd bundle that can be installed using pip or conda. If you have installed mystmd this way, uninstall it before using the local development instructions below.
:::

To do local development, clone the repository:

shell
git clone [email protected]:jupyter-book/mystmd.git
cd mystmd

Then, install dependencies via bun.
You need to do this each time you pull from upstream, since dependency versions may change:

shell
bun install

Now, build mystmd:

shell
bun run build

Optionally, you can link the built executable as your globally installed mystmd:

shell
bun run link

These commands allow you to use the myst CLI from any directory; source code changes are picked up after each bun run build (executed in the top-level source directory).

{warning} windows users should use unix-like shells
The build process uses unix commands that might not work properly on Windows.
When building on Windows, use either WSL or a unix-like shell (such as Git Bash or MSYS2).

(local-docs-workflows)=

Local docs workflows (preview/build)

To run a MyST server with your local changes and a preview of the documentation, run:

shell
$ bun run docs

This builds the mystmd packages, and starts a live-reloading preview server.

The docs/ plugins import several myst-* packages as workspace:* dependencies, so that they build against the source versions in this repository (not against npm). If you only change the plugins in docs/*.mjs, you will need to rebuild the packages and restart the server.

Local development: myst-theme

The myst-theme README provides a more detailed overview of the components of that package.

There are two ways to deploy a theme (AKA React web app): as a server, or as a static build.
Development procedures are slightly different, depending on which approach you take.

Approach 1: Theme server

Recall from the architecture overview that myst-theme is a React web application. It provides theming, and requires a separate content server for data. When developing, the steps are therefore to:

  1. Launch a content server
  2. Launch the myst-theme web application server (this is what you browse to)
  3. Run a process to monitor changes and rebuild myst-theme

Content server

We need some example data to test our theme against, such as the example landing page. Clone this example content repository and start the content server:

shell
git clone https://github.com/jupyter-book/example-landing-pages
cd example-landing-pages
myst start --headless

The --headless flag tells myst not to start a theme server; we want to do that ourselves in the next step.

When you start a content server without a theme server, you can still "visit" the pages in your site (often on port 3100). If you do so, you will see raw JSON and images. These represent the AST that the content server produces, and that a theme server uses to render a website (in the next step).

myst-theme server

We now fire up the myst-theme React app. This app server fetches the AST JSON from the content-server, then converts it to HTML, and serves it to the client where it is hydrated.

First, clone the myst-theme repository and install dependencies:

shell
git clone https://github.com/jupyter-book/myst-theme/
cd myst-theme
bun install

Then, see if you can build the package:

shell
bun run build

After the build succeeds, launch the theme server:

shell
bun run theme:book

After a while, it should tell you that the server has been started at http://localhost:3000. Browse there, and confirm that you can see the landing-page content.

Each time you change the myst-theme source, you must recompile by re-running bun run build. This allows you to preview the changes locally.

To automatically watch for changes and reload, use the following command:

shell
bun run dev

Note that you can run bun run dev from within any folder if you'd like to watch individual packages instead of the entire directory structure.

Approach 2: Static build

No content or theme server is required for a static site build. Steps are:

  1. Build the theme for production
  2. Point your site config to the built theme
  3. Build the site statically

We'll use the mystmd docs site as an example.

Under the hood, mystmd build --html starts myst-theme as a local Remix server, fetches each route as HTML, writes the results to disk, and runs a small post-processing pass (see packages/myst-cli/src/build/html/index.ts).

Build theme

To build a static site against a local theme, the theme must be built as it would be for production. For that we will use the "make" target
instead of bun run:

{code} bash
cd myst-theme
make build-book # because mystmd docs site uses the book theme

That should produce a production ready version of the book theme under .deploy/book.

:::{tip}
While debugging, it helps to work with a theme built against the "development" version of React, which results in unminimized build artifacts (ie preserving whitespace and variable/function names) and more detailed React errors and warnings. For this, tell remix to run in node "development" mode:

{code} json
:filename: myst-theme/themes/book/package.json
// Add NODE_ENV near the end of the "prod:build" script
                      v
"prod:build": "... && NODE_ENV=development remix build",
                      ^

:::

Configure site

Building a static site against a local theme requires configuring your site's myst.yml to point to the built theme's template.yml. Using the mystmd docs site as
an example:

{code} yaml
:filename: mystmd/docs/myst.yml
site:
    # assuming your mystmd and myst-theme working directories are siblings
    template: ../../myst-theme/.deploy/book/template.yml

(NOTE: This is the built template file, under .deploy, not the source template)

Build site

{code} bash
cd mystmd/docs
mystmd build --html

Then host the static site locally. An easy way is with Python's built-in http server:

{code} bash
cd _build/html
python3 -m http.server  # serves on port 8000 by default

Finally, browse the site at http://localhost:8000.

Step Debugging

Sometimes the trusty console.log (aka print statement) is not sufficient for your debugging needs. In more involved situations, a proper step debugger can be your friend.
You may have used Firefox or Chrome developer tools to debug in-browser code by adding the debugger keyword into your javascript source.
But what about code that runs in node engine on the server?
Chrome and Firefox dev tools can also debug server-side javascript. To do this, node must be invoked with the --inspect
or --inspect-brk comand-line option. That will cause the node process to open a socket listening for a debugger to connect. If "inspect-brk" is used
(vs "inspect"), then the process will immediately pause, awaiting a debugger connection. The debugger keyword in source will only pause the program if a
debugger is connected when that keyword is reached.

To connect to an awaiting node debug socket, enter into the browser address bar:

  • Firefox: about:debugging, or
  • Chrome: chrome://inspect

That opens a debugger control panel in the browser which should list your awaiting node process as available to connect to (if it has been correctly invoked).
(NOTE: other browsers and IDEs can also connect as debug inspectors - see Inspector Clients reference below)

In most cases we don't call node directly, so there is no easy way to add those options to a shell invocation.
Instead, a script can be defined with environment variable NODE_OPTIONS=--inspect (or "--inspect-brk") set in an appropriate spot (example follows).

Debug myst-theme service

For example, to debug myst-theme's book theme running as a dynamic application server, alter the scripts like so:

{code} json
:filename:  myst-theme/themes/book/package.json
// add NODE_OPTIONS
//                                                                                    v
"dev": "bun run dev:copy && bun run build:thebe && concurrently \"bun run dev:css\" \"NODE_OPTIONS=--inspect-brk remix dev\"",

Now remove the turbo "--parallel" option, because when turbo runs the theme server in "parallel" (ie multiprocess) mode, each process will attempt to open its own debug socket on the same port, with all but the first failing.
Then your debugger will only connect to the one that succeeded, with no guarantee any subsequent web request will hit the one process connected to the debugger.

{code} json
:filename:  myst-theme/package.json
// remove the --parallel option
//                           v
"theme:book": "turbo run dev --filter='./themes/book'",

Debug static myst-theme

To debug myst-theme running server-side in service of a static build:

TBD

Debug a mystmd subpackage test suite

This requires a handful of options to be passed to vitest to ensure it runs in a single process. For example, to connect a step debugger to a running myst-transforms test:

{code} json
:filename: mystmd/packages/myst-transforms/package.json
"test": "vitest --inspect-brk --pool threads --poolOptions.threads.singleThread --no-file-parallelism run",

Debugging references

Infrastructure we run

(myst-api-server)=

The MyST API server

We run a lightweight server at api.mystmd.org to help users resolve and download templates. The code for this exists at the myst-templates/templates repository.

For example, to get a list of template types (i.e. the available output types) you can GET this URL:

https://api.mystmd.org/templates

And to see a list of available templates that can be resolved for Typst, you can GET this URL:

https://api.mystmd.org/templates/typst

(howto:release)=

How to make a release

Here are the basic steps to follow for any project:

  1. Coordinate the release with the team.
  2. Publish the release in the package repository for the tool. For example:
  3. Confirm that GitHub made a release.
  4. (optionally) Create and share a blog post.

(release:coordination)=

Coordinating the release with the team

We use the #release_coordination Discord channel to coordinate and collaborate on releases.
This is a way to generate excitement and get any last-second work in before shipping.
It also gives others a heads-up that some new functionality is about to go live.

Publish a release for the repository

When you publish a release, you upload a new version of the tool for package managers to use. The specifics depend on the toolchain for your package (in particular, it is different for Python vs. JavaScript tools). Here are a few specifics for the mystmd repository.

(release:mystmd)=

Publish a `mystmd` release to NPM, PyPI and Conda

mystmd is published to NPM via a GitHub Action. Here's how to publish a release.

  • Find the changesets PR. This contains a list of the version updates that will be included with this release. Here's an example of a release PR.
  • Review the changeset PR.
    • Ensure that either mystmd or myst-cli is in the changesets.
      Or, ensure you are intentionally not releasing mystmd (this generally shouldn't happen), in which case the python and release notes are expected to fail.
      See for more explanation.
    • Ensure that private or non-existent packages like docs, etc. are not in the changesets (these will cause an early failure)
    • Ensure that there are no new myst packages that need to be published (see )
  • Merge the changesets PR. After merging that PR, this GitHub action will make a release.
    • It calls bun run version to generate the changelog (to review the changelog, you can run that command locally too).
    • It then publishes the updated packages to the mystmd npm registry (it calls bun run publish:ci, which calls changeset publish).
    • It creates multiple git version tags (which you'll use in making the GitHub release).
    • It releases to PyPI
    • The release notes are created automatically based on the mystmd@v... tag
  • Confirm that NPM, PyPI versions are released and a GitHub release has been made. Once this happens and there are no errors, you're done!
  • A short time later, a Conda PR is opened automatically and needs to be merged.

Fixing errors during a release

Ask for help in the #release_coordination Discord channel. The exact fix will depend where in the automated release things broke. Some tips:

  • In general, do not revert a release PR, instead fix the errors (permissions, etc. with NPM/tokens) and merge any necessary fixes to main.
  • A mystmd tag at the right version is necessary for the release notes to run
  • A PyPI release is only triggered if there is a release triggered by NPM
  • Partial NPM releases (e.g. due to permission failures or token access) can be rerun without a new changeset. In this case you can:
    • update the token/connection and rerun the action
    • fix a package.json (e.g. add a git trusted publisher), merge to main, and add a tag for mystmd (git tag [email protected], for the release notes) if that package isn't being re-released
  • A new NPM package has been created but not created or linked:
    • Create the package, add ebp-bot to it, setup trusted publishing in NPM.

(release:new-package)=

Publishing a new NPM package for the first time

If a PR introduces a new package, you must publish it to NPM manually before merging the changesets PR.
For example, PR #2602 and its myst-theme counterpart added the new anywidget package.

Before proceeding, ask about this in #release_coordination Discord channel and ensure that we are (a) about to make a release; and (b) there are no last-minute changes to the package name. Creating a new NPM package cannot be undone and packages cannot be renamed.

Steps to publish a new package:

  1. Build and test the package to confirm it works as expected.
  2. Set the version to 0.0.0 in the package's package.json. Changesets will overwrite this on the first automated release.
  3. Publish the package manually with public access:
    shell
    npm publish --access=public
  4. Set up trusted publishing in the NPM admin so that GitHub Actions can publish future releases automatically.
  5. Add ebp-bot as a maintainer on the NPM package page. This is not needed if the package belongs to a team (e.g., @myst-theme).
  6. Announce the new package in the #release_coordination Discord channel so the team is aware and comment on the relevant GitHub Release PR that this package has been created.

(release:myst-theme)=

Publish a release of `myst-theme`

The process for releasing myst-theme infrastructure is similar to the release process for mystmd. Here's a brief overview:

(release:github)=

Automated releases on GitHub

After a successful NPM release, our release.yml workflow will automatically create a new GitHub Release.
The release notes are generated using github-activity and include a summary of all merged PRs and commits since the previous release.

Maintainers are encouraged to review the new GitHub Release and edit the description as needed.
For example, you may want to add an executive summary, highlight important changes, or clarify upgrade instructions for users.

You can find the latest releases at: https://github.com/jupyter-book/mystmd/releases

(release:docs)=

Update documentation

For tools hosted at mystmd.org: when we make a release of mystmd, the MyST documentation is automatically updated via this GitHub Workflow. You can also trigger a manual release by following .

For most other tools, the documentation should auto-update with new releases (e.g., via a GitHub action).

(release:share)=

Create and share a blog post

Feel free to use the blog to post release notes as well.
This should be as simple and quick as possible, don't over-think it. Here are some simple steps:

  1. Copy and paste the release notes you generated into a new file on our blog repository.

  2. Use the following YAML header metadata for the post:

    yaml
    ---
    date: <release-date>
    author: The Jupyter Book Team
    title: <title from GitHub release>
    tags: release
    ---
    
    <content from GitHub release>
  3. Self-merge the blog post.

Feel free to celebrate new releases and share the news publicly so that others know about new enhancements in our projects.

Spaces to publicize releases. Here are a few places that are worth sharing (you can just copy/paste the same text into each):

Practices we follow

Build system

We use Turbo to manage our testing and build system.

(developer:testing)=

Testing

We use vitest for all tests in mystmd.

How to run the test suite

First install mystmd locally.

Then, run the test suite using:

shell
bun run test

If you are working in a particular package, change your working directory to that specific package, and run the tests there.

shell
cd packages/myst-cli
bun run test

To run in "watch mode" (runs each time a change is saved), use:

shell
bun run test:watch

To run a single test by matching its title, first change the working directory to the package where the test exists. For example:

shell
cd packages/mystmd

Then run the test command like so:

shell
bun run test -- -t "Basic tex build"

For integration test titles, see packages/mystmd/tests/exports.yml.

Types of tests

There are two types of tests in the mystmd repository:

Unit Tests - are attached to each sub-package in the packages/ directory. These use vitest for basic functionality within each package. They have files like config.spec.ts.

Integration Tests - Test the entire mystmd build workflow. Here's how it works:

  • packages/mystmd/tests/endToEnd.spec.ts runs a build from end to end, similar to the vitest tests above.
  • packages/mystmd/tests/exports.yml contains several integration tests - each one points to a build configuration (a folder in packages/mystmd/tests/) and compares expected to generated outputs.

Linting

When contributing code, continuous integration will run linting and formatting on your pull request.
You can also run bun run lint and bun run lint:format locally to catch errors early. To automate that process for each commit, install the git pre-commit hook: bun run install-pre-commit.[^uninstall-pre-commit]
If you are using the VSCode editor, it can be setup to show changes in real time and fix formatting issues on save (extensions: eslint and prettier).

[^uninstall-pre-commit]: Uninstall the pre-commit hook with git config --unset core.hooksPath.

Running in live-changes mode: depending on the package you are working in we have also setup live changes which can be faster than the bun run build; this can be run using bun run dev. If your changes aren't showing up, use bun run build as normal.

Versioning and changesets

We use changesets for tracking changes to packages and updating versions.
To learn about changesets, see this introductory guide to changesets.
We use this changesets GitHub Action when we publish a release.

Before submitting your Pull Request, please add a changeset using bun run changeset, which will ask you questions about the package and ask for a brief description of the change.
Commit the changeset file to the repository as a part of your pull request.
You can use bun run version to preview the generated changelog.

Our current versioning procedure is a little loose compared to strict semantic versioning; as mystmd continues to mature, this policy may need to be updated.
For now, we try to abide by the following rules for version bumps:

  • major: Backward incompatible change to the underlying supported MyST data. These would be cases where a non-developer MyST user's project or site built with major version N would not work with major version N+1. Currently, we never intentionally make these changes.
  • minor: Backward incompatible change to the JavaScript API, for example, changing the call signature or deleting an exported function. These can be a headache for developers consuming MyST libraries, but they do not break MyST content.
  • patch: For now, everything else is a patch: bug fixes, new features, refactors. This means some patch releases have a huge, positive impact on users and other patch releases are basically invisible.

(changesets:myst-cli)=

What if changesets doesn't bump `mystmd` or `myst-cli`?

The auto-generated changesets release PR must list at least one changeset that bumps mystmd or myst-cli, or its CI will fail.
This is because we almost always want to release a new mystmd on merge, which only happens if one of these is bumped.

If you merge the changesets PR when neither is bumped, the release job of mystmd-py to PyPI will fail, and a GitHub release won't be made for the repository.
The packages that we did update will still be uploaded to NPM.
Here's an example of this happening: mystmd#2857.

You can still merge the Changesets PR if you want!
If you intentionally don't want a mystmd bump on this release, override the failing check and merge the release PR anyway.
NPM still publishes everything else, and the PyPI and GitHub release jobs are an expected failure with nothing to fix afterwards.

Packages in the mystmd repository

All packages used to build mystmd live in the https://github.com/jupyter-book/mystmd repository.

{note}
`myst-theme` is *not* part of the `mystmd` package, and lives at https://github.com/jupyter-book/myst-theme.

These packages are ESM modules[^ESM], written in TypeScript, and live at https://github.com/jupyter-book/mystmd/tree/main/packages.

[^ESM]: ESM modules are imported using the JavaScript import syntax, and cannot be used with CommonJS require().

Command Line Tools:

  • mystmd: provides CLI functionality for myst start.
  • mystmd-py: a Python wrapper around mystmd, which makes it easy to install the tool in a Python environment. It should not be used for development.

Core Packages:

  • myst-cli: provides CLI functionality for mystmd. It does not export the CLI directly.
  • jtex: a templating library (see docs).
  • myst-frontmatter: definitions and validation for scientific authorship/affiliation frontmatter (see docs).
  • myst-config: validation and reading of configuration files.
  • myst-templates: types and validation for templates (LaTeX, web, and word).

Markdown Parsing

MyST parsing builds on the markdown-it parser.
markdown-it handles "standard" syntax (paragraphs, bold, links, etc), and markdown-it-myst adds rules for MyST-specific syntax (e.g., roles and directives).
The MyST engine first uses markdown-it to parse raw text into tokens, and then myst-parser converts them into the MyST AST (using mdast).

  • markdown-it-myst: markdown-it plugin to handle tokenizing roles and directives.
  • myst-directives: core directives for MyST.
  • myst-roles: core roles for MyST.
  • myst-parser: converts the markdown-it token stream to MyST AST.

Readers

  • tex-to-myst: convert LaTeX to MyST AST.
  • jats-to-myst: convert JATS XML, a standard for representing papers, to MyST AST.

Transformers

  • myst-transforms: transformations for use with MyST AST to transform, e.g., links, citations, cross-references, and admonitions (see here for more information).

Export Tools

  • myst-to-docx: convert MyST documents to MS Word format.
  • myst-to-jats: convert MyST to JATS, for use in scientific archives.
  • myst-to-tex: convert MyST to LaTeX, to be used in combination with our jtex template compiler, to create stand alone LaTeX documents.
  • myst-to-html: convert MyST to HTML.

Extensions:

  • myst-ext-card: Card directives.
  • myst-ext-grid: Grid directives.
  • myst-ext-tabs: Tab directives.
  • myst-ext-reactive: Reactive directives.

Miscellaneous:

  • myst-common: common utilities for working with ASTs.
  • myst-spec: JSON Schema that defines the MyST AST.
  • myst-spec-ext: temporary development extensions to myst-spec.
  • citation-js-utils: utility functions to deal with citations.
  • myst-cli-utils: shared utils between jtex, and myst-cli.
  • simple-validators: validation utilities, that print all sorts of nice warnings.

Tips and tricks

Grepping the codebase

While developing, there are a lot of build artifacts lying around,
making grepping the code harder than it needs to be. Assuming you
have
ripgrep
or a compatible alternative installed, you can define a customized
"myst grep" in your shell config (~/.zshrc, ~/.bashrc, etc.):

sh
alias mg="rg --exclude-dir=build --exclude-dir=_build -g '!*.js' -g '!tailwind.css'"

Most notably, this excludes .js files, which are generated from TypeScript.


Diagrams (`docs/diagrams.md`)


title: Diagrams description: Include simple programmatic mermaid diagrams in your documents. thumbnail: ./thumbnails/diagrams.png

It is possible to add mermaid diagrams using the {myst:directive}mermaid directive, for example:

{myst}
```{mermaid}
flowchart LR
  A[Jupyter Notebook] --> C
  B[MyST Markdown] --> C
  C(mystmd) --> D{AST}
  D <--> E[LaTeX]
  E --> F[PDF]
  D --> G[Word]
  D --> H[React]
  D --> I[HTML]
  D <--> J[JATS]
```

:::{note}
Both GitHub and JupyterLab (#101) support the translation of a code-block ```mermaid to a mermaid diagram directly, this can also be used by default in MyST.
:::


Directives (`docs/directives.md`)


title: Directives label: directives_list

To learn more about the syntax and usage of the directives, please refer to the section of the documentation.

:::{myst:directive} admonition
:::

:::{myst:directive} aside
:::

:::{myst:directive} bibliography
:::

:::{myst:directive} blockquote
:::

:::{myst:directive} code
:::

:::{myst:directive} code-cell
:::

:::{myst:directive} csv-table
:::

:::{myst:directive} dropdown
:::

:::{myst:directive} embed
:::

:::{myst:directive} figure
:::

:::{myst:directive} glossary
:::

:::{myst:directive} iframe
:::

:::{myst:directive} image
:::

:::{myst:directive} include
:::

:::{myst:directive} list-table
:::

:::{myst:directive} math
:::

:::{myst:directive} mdast
:::

:::{myst:directive} mermaid
:::

:::{myst:directive} myst
:::

:::{myst:directive} table
:::

:::{myst:directive} toc
:::


Document Parts (`docs/document-parts.md`)


title: Document Parts

Document parts allow you to add content and metadata for specific components of your page or project, including example, abstract, dedication, and acknowledgment. Templates may use the parts and their metadata, and render such content in various locations.

Add document parts

Document parts can be used in any export media (html, pdf, etc).
There are several ways you can define document parts, each described below:

  1. In project-level configuration
  2. In page frontmatter
  3. With a content block
  4. With specific section headings
  5. With Jupyter Notebook cell tags

(parts:project)=

Parts in `myst.yml` Project configuration

You may also specify parts in the project configuration of your myst.yml file. These are defined exactly the same as parts defined in page frontmatter.

yaml
version: 1
project:
  abstract:  |
    This is a multi-line
    abstract, with _markdown_!
  parts:
    special_part: |
      This is another _special_ part!

Project-level parts are useful, for example, if you have an abstract, acknowledgments, or other part that applies to your entire project and doesn't make sense attached to an individual page.

{caution}
Project-level `parts` are a new feature and may not yet be respected by your chosen MyST template or export format. If the project `part` is not behaving as you expect, try moving it to page frontmatter for now.

(parts:frontmatter)=

Parts in Frontmatter

On any page, you can add a part to your document directly in the frontmatter, for example, the abstract:

yaml
---
title: My document
abstract: |
  This is a multi-line
  abstract, with _markdown_!
---

You may also write your part in a separate file, and point to that file from the frontmatter, for example:

yaml
---
title: My document
abstract: ../abstract.md
---

(parts:blocks)=

Parts with content blocks

You can use content blocks in a page to define a part. This will be parsed differently from the other content on the page for re-use in templates and websites.

The following example shows how to define an abstract part in a content block on a page.

{code} markdown
:filename: mypage.md

# Page title

+++ { "part": "abstract" }

This is my abstract block.

+++

Page content

(parts:implicit)=

Parts with specific header titles

If you are rendering your project in other places, it can be helpful to leave these sections directly in the document.
Complete this using a header as usual:

text
# Abstract

This is my abstract!

Note that frontmatter parts and explicitly tagged cells/blocks will take precedence over this method. Themes may choose to only pick up a subset of implicit parts, for example, only an Abstract and not Summary as summary section can be used in other contexts.

(parts:cell-tags)=

Parts with Jupyter cell tags

When using a Jupyter Notebook, you can add a tag to the cell with the part name. If multiple cells share that tag, they will be extracted and merged.

Known Document Parts

The known parts that are recognized as top-level document parts keys are:

abstract
: A concise overview of the entire document, highlighting the main objectives, methods, results, and conclusions. It's meant to give readers a quick snapshot of what to expect without having to read the entire document.

summary
: Similar to an abstract, but can either be slightly longer and more detailed or used as a plain-language summary, depending on the context. It summarizes the document's content, including the background, purpose, methodology, results, and conclusions.
: Alias: plain_language_summary, lay_summary

keypoints
: A brief list that highlights the main findings, conclusions, or contributions of the document. Key points are often used to quickly convey the core message or most important aspects to the reader.

dedication
: A short section where the author dedicates the document to someone, often as a gesture of honor or respect.

epigraph
: A quote or poem that the author includes at the beginning of the document to set a tone or theme, or to hint at the document’s underlying message. It is often relevant to the content but not directly related to it.
: Alias: quote

data_availability
: A statement or section that details how readers can access the data sets and resources used in the document. This can include links to repositories, conditions for access, and any restrictions on the data. It's crucial for transparency and reproducibility in research documents.
: Alias: availability

acknowledgments
: A section where the author thanks individuals, organizations, or agencies that contributed to the completion of the document. This can include support in the form of funding, expertise, feedback, or moral support.
: Alias: ack, acknowledgements

Custom Frontmatter Parts

If you have a custom part name for a template, you can nest it under parts:, which takes arbitrary keys.

yaml
---
title: My document
parts:
  special_part: |
    This is another _special_ part!
---

The advantage of this method is that the content is not rendered in your document.

(parts:site)=

Add website parts

Website themes have additional parts because they render user interface elements that are not part of a standard "document" structure. These are theme-dependent: for example, the default myst themes support a footer part and banner part.

Specify the content of a site part in the site.parts key of the myst.yml configuration file:

{code} yaml
:filename: myst.yml
version: 1
site:
  template: ...
  parts:
    footer: |
      (c) MyST Markdown
    banner: My banner [content](https://mystmd.org)!
  ...

Alternatively, you may specify a path to a file that contains the part content:

{code} yaml
:filename: myst.yml
version: 1
site:
  template: ...
  parts:
    footer: parts/myfooter.md
    banner: _site/banner.md
  ...

You can also specify a URL to a remote markdown file:

{code} yaml
:filename: myst.yml
version: 1
site:
  template: ...
  parts:
    footer: https://example.com/shared-footer.md
  ...

Content in parts will generally be parsed similarly to other MyST content (though some functionality like code execution will not work).

Share the same part across multiple sites

If you wish to share the same part across multiple sites, use the extends: key to compose multiple configuration files. This lets you define the part in one location, and re-use it in several others. It is useful if you want to standardize website components like a footer across many websites.

For example, in one file:

{code} yaml
:filename: parts.yml
site:
  parts:
    footer: |
      My nifty footer!
  ...

And in another:

{code} yaml
:filename: myst.yml
version: 1
extends: parts.yml
site:
  template: ...

If you're extending configuration from a remote source, make sure that you use absolute URLs if you must refer to an image or other asset in your part content. Relative paths that are defined inside the part content will generally break.

:::{seealso} Referring to parts directly with a URL is coming
See the issue tracking this enhancement.
:::

(parts-ast)=

How parts appear in the AST

Each way of defining a part lands in a different place in the JSON that myst build emits.
This section describes how these are represented in the AST.
See for more details about the MyST AST.

Page-level parts in the page AST

There are three ways to mark a region as a named part in a document.
Each one ends up in a different place in the served JSON, even though they are often treated the same in the rendered output (see for guidance in parsing them).
We show each method below, and where it lands in the AST:

{list-table}
- - Method
  - Example
  - Location in AST
- - **Page frontmatter** (in the `parts:` key)
  - ```markdown
    ---
    parts:
     acknowledgments: "..."
     abstract: "My abstract"
    ---
    My content...
    ```
  - Stored in the page frontmatter key:
  
    `frontmatter.parts.<partname>.mdast`.
- - **Block metadata** (within content of a page)
  - ```markdown
    +++ {"part": "abstract"}

    My abstract content.

    +++

    My content

    +++ {"part": "acknowledgements"}
    Thanks to ...
    ```
  - Stored in a `block` node inside `mdast` with `data.part` set.

    `.mdast.children.<block>.data.part = "acknowledgements"`
- - **Heading auto-match** (heading text equals a known part name)
  - ```markdown
    ## Abstract

    My abstract ...

    ## Header one

    A normal (non-part) header

    ## Acknowledgments

    Thanks to ...
    ```
  - Remains a regular `heading` + paragraphs sequence.
    Only becomes a part block when [`extractPart()`](#ast-parse-helper) is called.

Site-level parts in the project AST

Site-level parts: (under site: in myst.yml) is a different mystmd-specific system for content shared across pages (like a project footer or header).
These are project-level and not part of any page's AST.
The theme developer guide covers the rest of what mystmd serves alongside per-page JSON.

(ast-parse-helper)=

Programmatically extract a part from an AST

Instead of manually parsing the page AST for these parts, use the extractPart() helper to retrieve a named part regardless of how it was defined.

For example, exporters like myst-to-jats call extractPart() to pull these regions into their output.


Documents Exports (`docs/documents-exports.md`)


title: Exporting overview

You can export MyST content into one or more static documents, and optionally bundle them with a MyST website. This section gives an overview of the Exporting process and major configuration options.

(types-of-exports)=

List of formats you can export

Below are supported export types and links to documentation for further reading:

{list-table} frontmatter export formats
:header-rows: 1
:label: table-export-docs
- * Export type
  * Learn more
- * `pdf`
  * [](./creating-pdf-documents.md)
- * `tex`
  * [](./creating-pdf-documents.md)
- * `typst`
  * [](./creating-pdf-documents.md)
- * `JATS`
  * [](./creating-jats-xml.md)
- * `Microsoft Word`
  * [](./creating-word-documents.md)
- * `CITATION.cff`
  * [](./creating-citation-cff.md)
- * `MyST Markdown`
  * [](#export:myst)

Where to configure options for exports

You can configure export options in two different places:

(exports-page-frontmatter)=
In page frontmatter:

{code-block} yaml
:filename: my-markdown-file.md
---
title: My PDF
exports:
  - format: pdf
    template: arxiv_two_column
    output: exports/my-document.pdf
---

In your myst.yml configuration. In this case you will need to specify the article (or articles) that you are targeting. For example, here's how you'd configure an export from a single-page document.

{code-block} yaml
:filename: myst.yml
version: 1
project:
  exports:
    - format: pdf
      template: arxiv_two_column
      article: my-markdown-file.md
      output: exports/my-document.pdf

(articles-or-article)=
:::{note} You can use either article: or articles:
MyST will let you use either one of these options, to make the myst.yml configuration more readable.
:::

Configuration options for exports

There are a few options you can use to configure exports:

{list-table}
- - Option
  - Description
- - `format`
  - The type of export you'll create. For examples, see [](#types-of-exports).
- - `template`
  - The template to use for the export. For more information, see [](#export-templates).
- - `output`
  - The output file to be created.
- - `id`
  - A unique identified for the output, in case you want to [re-use exports later](#reuse-export-outputs).
- - `articles`
  - One or more source files to use in the export (you can [use `article` as well](#articles-or-article)). If using [page frontmatter](#exports-page-frontmatter), it will default to the current document.
- - `toc`
  - If exporting from a multi-page book, the [Table of Contents](./table-of-contents.md) that defines the book structure.

(export-templates)=

Choose a template

The exporting process uses a MyST Template MyST Template to transform the {term}MyST AST into an output format. MyST Templates are written for a specific export format. A MyST Renderer converts MyST AST into components that a template can use to export a final output.

You can choose the template for a given export format with the template argument.
There are several template names you can use out of the box[^api-server].
For a list of community templates you can use, see the MyST Templates table in the [myst-templates] GitHub organization.

[^api-server]: These are resolved by the MyST API server.

You can also use a URL or a local path that points directly to a template. For example:

yaml
- format: typst
  id: typstpdf
  template: https://github.com/rowanc1/typst-book.git
  articles:
    - page1.md
    - page2.md
  output: ./_build/pdf/typst-report.pdf

The `myst-templates` GitHub organization

The myst-templates GitHub organization has a collection of MyST templates that are contributed and maintained by the community. These templates are made easily-consumable by MyST users via the MyST API service, see for technical details.

Build one or more exports

After defining exports in your frontmatter, you may build them with the myst build command, by default this only builds the site.
You can configure the CLI command in a number of ways:

myst build --all
: Build all exports in the project

myst build --pdf --docx
: Build pdf (LaTeX or Typst) exports and docx in the project

myst build my-paper.md
: Build all exports in a specific page

myst build my-paper.md --pdf
: Build all pdf exports in a specific page

(reuse-export-outputs)=

Re-use export outputs for download buttons

You can re-use exported artifacts by setting an id: in your export configuration.
Then, reference that ID in your downloads: configuration.

Export Configuration

The following table shows the available properties for each export. You must define at least one of format, output, or template for MyST to be able to perform your output. You may also specify a string instead of a full export object; this string will be inferred to be either the export format or the output filename. The table below is from .

{seealso} exposing exports as downloads
You can also include exported documents as downloads in your site, see [](./website-downloads.md).

Split your document across multiple content files

When writing longer documents like manuscripts, it's common to write your document in multiple parts and then stitch them together into a single narrative.
You can accomplish this in MyST with the {myst:directive}include directive.

See for more information.

(export:myst)=

Export MyST Markdown from another document format

MyST can parse some non-MyST document formats as well.
This makes it possible to convert something into MyST Markdown.

For example, to convert LaTeX into MyST Markdown, use the following command:

text
myst build doc.tex --md


title: Dropdowns, Grids, Tabs and Cards short_title: Grids, Tabs & Cards thumbnail: ./thumbnails/dropdowns-cards-and-tabs.png

(dropdowns)=

Dropdowns can be used to toggle content and show it only when a user clicks on the header panel. These use the standard HTML `` element, meaning they also will work without JavaScript. The dropdown can have a title, as the directive argument, and the open option can be used to initialise the dropdown in the open state.

{myst}
:::{dropdown} Dropdown Title
:open:
Dropdown content
:::
{seealso}
:class: dropdown

# Use "Admonition Dropdowns" for style!
You can also hide the body of your admonition blocks so that users must click a button to reveal their content. This is helpful if you’d like to include some text that isn’t immediately visible to the user.

To turn an admonition into a dropdown, add the option `:class: dropdown` to them. See [](#admonition-dropdown) for more information.

Cards

Cards provide an easy way for you to content into a standard “header”, “body”, “footer” structure that has a similar alignment and visual style. It is useful for creating galleries or high-visibility collections of links and information.
For example, a card with a header, title, body, and footer:

{myst}
```{card} Card title
:header: The _Header_
:footer: Footer

Card content
```

You can also add a link option to the card, which will allow you to make the entire card clickable:

{myst}
:::{card} Clickable Card
:link: https://mystmd.org

The entire card can be clicked to navigate to `mystmd.org`.
:::
{note} compatibility with sphinx design
:class: dropdown

In the [Sphinx design project](https://sphinx-design.readthedocs.io/en/latest/cards.html) card headers and footers take a special syntax:

- A card `title`: The argument given to the directive.
- A card `header`: Any content that precedes a line with ^^^.
- A card `footer`: Any content that comes after a line with +++.
- A card `body`: Any content that comes in between ^^^ and +++.

This syntax is supported in `mystmd`, for example:

```markdown
:::{card} Card Title
Header
^^^
Card content
+++
Footer
:::
```

Note that, card headers and footers are optional. If you don’t include ^^^ or +++ in your card, they will not show up.

:::{myst:directive} card
:::

(ui:buttons)=

Buttons

A button is an element with text content that triggers an action to navigate to an internal or external reference upon a user click. Use the {myst:role}button role followed by the text content and target path to create a button.

{myst}
{button}`MyST Role Spec <roles.md>`
{myst}
{button}`MyST-MD GitHub <https://github.com/jupyter-book/mystmd>`
{myst}
{button}`A button without a link!`

(grids)=

Grids

Grids allow you to structure arbitrary chunks of content in a grid-like system.

To generate a grid, use the :::{grid} ... wrapper directive along with :::{grid-item} or :::{card} directives inside.

The numbers supplied in the argument are column counts to be used on different screen sizes e.g. 1 1 2 3 corresponding to smallest (<768px), medium (768px – 1024px), large (1024px – 1280px), and extra-large screens (>1280px). These pixel widths are determined by the theme (e.g. the book theme), which uses the default Tailwind CSS breakpoints.

Example with grid-items:

{myst}
::::{grid} 1 1 12 12

:::{grid-item}
:columns: 3
This is one sentence. It occupies the narrower column.
:::

:::{grid-item}
:columns: 9
This is another longer sentence which requires more space. It occupies the wider column.
:::

::::

Example with cards:

{myst}
::::{grid} 1 1 2 3

:::{card}
:header: Text content ✏️
Structure books with text files and Jupyter Notebooks with minimal configuration.
:::

:::{card}
:header: MyST Markdown ✨
Write MyST Markdown to create enriched documents with publication-quality features.
:::

:::{card}
:header: Executable content 🔁
Execute notebook cells, store results, and insert outputs across pages.
:::
::::

:::{myst:directive} grid
:::

Tabs

You can also produce tabbed content. This allows you to display a variety of tabbed content blocks that users can click on.

{myst}
::::{tab-set}
:::{tab-item} Tab 1
:sync: tab1
Tab one
:::
:::{tab-item} Tab 2
:sync: tab2
Tab two
:::
::::

If you have multiple tabs with the same name, they will be synced!

{tab-set}
```{tab-item} Tab 1
:sync: tab1
Synced content for tab 1
```
```{tab-item} Tab 2
:sync: tab2
Synced content for tab 2
```

:::{myst:directive} tab-set
:::

:::{myst:directive} tab-item
:::


Embed (`docs/embed.md`)


title: Embed & Include Content

You can embed labeled content (paragraphs, figures, notebook outputs, etc) across pages, allowing you to avoid re-writing the same information twice. You can also include files that are not in your project.

::::{seealso} Creating a Label
Embedding content requires a label to be present.
To attach labels to blocks of content, see .
To attach labels to Jupyter Notebook content, see

For the examples below, we are defining a figure with a label:

markdown
:::{figure} https://github.com/rowanc1/pics/blob/main/mountains.png
:label: myLabel
Here's a cool figure.
:::

:::{figure} https://github.com/rowanc1/pics/blob/main/mountains.png
:label: myLabel
Here's a cool figure.
:::
::::

(docs:embed)=

The {myst:directive}`embed` directive

The {myst:directive}embed directive allows you to insert snippets of content at the time a page is rendered.

See {myst:directive}the {embed} directive documentation for details about all the arguments you can give to {embed}.

The {myst:directive}embed directive can be used like so:

myst
```{embed} #myLabel
```
{embed} #mylabel

Note that this works for any labeled item in your MyST document, including from other pages.
For example, the following references the admonitions list in .

text
```{embed} #admonitions-list
```
{embed} #admonitions-list

(embed-image-short-hand)=

The `` short-hand

The embedding shorthand lets you embed content using the Markdown image syntax (see more about images).
This removes the input cell if you are embedding from a Jupyter notebook.

It can be used like so:

markdown

Embed images into figures

If you have a labeled image in your documentation, you can embed it as a Figure so that it contains figure metadata (like a caption, or adding alt-text).
To do so, use a label attached to an image instead of a filepath.

For example, we'll define an image with a label below:

text
(nice-sunset)=

(nice-sunset)=

And embed it into a figure next with a new label:

text
```{figure} #nice-sunset
:label: sunset-figure
Here's a nice sunset with a caption!
```
{figure} #nice-sunset
:label: sunset-figure
Here's a nice sunset with a caption!

The new label can be referred to in this context, i.e. [@sunset-figure]: [@sunset-figure], which refers to the new figure rather than the original image. This allows you to scroll to embedded content on the page, rather than jumping to the original document. Note that this is especially useful with embedding Jupyter Notebook outputs. For example:

{figure} #img:altair-horsepower
This figure has been included from a Jupyter Notebook and can be referred to in cross-references through a different label. See [](./reuse-jupyter-outputs.md) for more information.

Embed notebook content and outputs

You can embed notebook content (for example, images generated by running a cell).
For instructions on how to embed notebook content, see .

Embed from External MyST Projects

You can embed content from an external site, allowing you to re-use snippets of text, figures and tables, etc from other MyST projects. This is similar to external cross-references, which allow you to hover over a link and see the content on a different MyST site.

First, add references to your myst.yml:

:::{embed} #myst-xref-config
:::

You can then refer to content in these sites in two ways:

  1. The embed short hand:

    md
  2. The {myst:directive}Embed directive :

    md
    :::{embed} xref:spec#admonition
    :::

When you build your MyST project, the content that your #label points to will be embedded in-place of the directive.

:::{proof:example} Embedding from the MyST Spec

The following content is embedded from ``:

:::

(docs:include)=

The {myst:directive}`include` directive

You can include multiple files into your MyST document as if they were all written in the same file.
This allows you to store content in separate files, and then weave them together at build time.
To do so, use the {myst:directive}include directive.

This is also helpful for including content snippets, such as a table or an equation, that you want to keep in a different file on disk, but present as if it were one document. In addition to Markdown, MyST will also parse .ipynb, .tex, and .html.

See {myst:directive}the {include} directive documentation for details about all the arguments you can give to {include}.

:::{proof:example} Equation Bank
:label: eg:equation-bank
It is common practice to keep complex equations out of the main document so that they can be shared between slides, papers, and different renderings of a document. The equations folder in our documentation has a curl.tex file:

{literalinclude} equations/curl.tex
:filename: equations/curl.tex

We can include that content in this document using:

markdown
```{include} equations/curl.tex

```

which includes the content with the LaTeX parser:

{include} equations/curl.tex

You will notice that there is no difference visually in the content, it is as if the content were include directly in line in the source document.
:::

:::{warning} Relative Paths in Markdown vs LaTeX
The {myst:directive}argument of the include directive is the file path and is relative to the file from which it was referenced. When working in markdown, recursive includes follow that same pattern. In LaTeX, however, recursive includes are relative to the original source file. This difference is only apparent when you have nested imports and a non-flat folder structure. MyST will also give you helpful warnings if it cannot find the file you are referencing.
:::

By default the file will be parsed using MyST, you can also set the file to be {myst:directive}include.literal, which will show as a code-block; this is the same as using the {myst:directive}literalinclude directive which is documented in .

:::{note} Auto Reload & Circular Dependencies
:class: dropdown
If you are working with the auto-reload (e.g. myst start), the file dependencies are auto-reloaded.
Circular dependencies are not allowed and MyST will issue a warning and not render the recursion.
:::

:::{tip} Math and abbreviation frontmatter from included files
:class: dropdown
When including a file that has frontmatter, only some of that frontmatter is used. For example, the math macros and abbreviations are brought up to the top level and will overwrite any macros or abbreviations that already exist.

For LaTeX, the commands like \newcommand and \renewcommand are shared in the same way that math macros are shared for markdown files.
:::

`{embed}` vs. `{include}`

The {myst:directive}include directive is very similar to {myst:directive}embed, with a few key differences.

{include} and {literalinclude}
: parses source files (e.g. text files on your filesystem) and inserts them into the document structure as if you had written the content in your target source file. These files are not listed in your project table of contents, and generally only contain snippets.

{embed}
: Pulls any labelled MyST content or outputs already parsed in your project.


Executable Plugins (`docs/executable-plugins.md`)


title: Write plugins in other languages

MyST is able to invoke plugins written in different languages through standard IO protocols, for example, in Python. These are called Executable Plugins because they rely on scripts that are executed at build time.

(executable-plugins)=

How executable plugins work

Executable Plugins use STDIO to allow plugins that are written in any language, so long as they can access STDIO and can be executed. You configure an executable plugin in myst.yml like so:

{code} yaml
:filename: myst.yml
project:
  plugins:
    - type: executable
      path: picsum.py

Your Executable Plugin file should have a PLUGIN_SPEC variable that defines its name and any roles, directives, or transforms that it will use. See this PLUGIN_SPEC for example.

Your Executable Plugin should be executable from the command line, and take the arguments --role, --directive, and --transform. These will be used by MyST. Here's an example in Python.

When your MyST site is built, MyST will execute the file specified in path: and follows a process like the following:

:::{warning} What happens in an Executable Plugin is a black box
Executable MyST plugins are treated as a , whereby MyST only sees the data it passes to the plugin, and the response from the plugin itself.
:::

Define a new directive

:::{note}
There are many different ways to create an executable plugin. Here we'll use Python to implement an picsum-py directive, but any programming language that can read and write from stdin, stdout, and stderr, and define a command line interface would work.
:::

First, we'll declare the plugin specification that allows MyST to discover the directives, transforms, and/or roles that the plugin implements. This specification looks very similar to the definition of a JavaScript plugin, except the implementation logic (e.g. the directive run method) is not defined.

:::{literalinclude} picsum.py
:caption: A plugin to add an picsum-py directive that includes a beautiful, random picture based on a size and optional ID string.
:::
this file should be executable, e.g.

{code} shell
chmod +x ./picsum.py

and should be referenced from your myst.yml under the projects.plugins:

{code} yaml
:filename: myst.yml
project:
  plugins:
    - type: executable
      path: picsum.py

then start or build your document using myst start or myst build, and you will see that the plugin is loaded.

text
myst start
...
🔌 Lorem Picsum Images (picsum.py) loaded: 1 directive
...

you can now use the directive, for example:

markdown
:::{picsum-py}
:size: 600x250
:::

:::{picsum-py}
:size: 600x250
:::

If you change the source code you will have to stop and re-start the server to see the results.

The types are defined in myst-common (npm, github) with the DirectiveSpec and RoleSpec being the main types to implement.

Implementing a custom transform

Directives can be used to extend MyST with rich structured content. However, sometimes we want to modify the existing behavior of MyST. One of the ways to do this is by writing a custom transform. In this section, we'll implement a transform that replaces bold text with special bold text.

First, let's define the transform
:::{literalinclude} markup.py
:caption: A plugin to add a transform that replaces strong nodes with emphasis nodes.
:::
this code should be referenced from your myst.yml under the projects.plugins:

{code} yaml
:filename: myst.yml
project:
  plugins:
    - type: executable
      path: markup.py

then start or build your document using myst start or myst build, and you will see that the plugin is loaded.

text
myst start
...
🔌 Strong to emphasis (markup.py) loaded: 1 directive
...

you can now use the directive, for example:

markdown
I am **special bold text (python)**, whilst I am **normal bold text**

I am special bold text (python), whilst I am normal bold text


Execute Notebooks (`docs/execute-notebooks.md`)


title: Execute Notebooks at Build Time subtitle: Generate figures and other rich content using Jupyter kernels short_title: Execute During Build thumbnail: thumbnails/execute-notebooks.png

:::{warning} MyST execution features are in Beta
By default, execution is disabled and computational outputs are only inserted if the notebook has already been executed (for text-based notebooks, there are no outputs!).
We are adding support for executing markdown notebooks and ipynb files, including inline execution.
As we are adding this functionality we appreciate any feedback from the community on how it is working in your environments. Please add issues or join Discord to give feedback.
:::

The MyST CLI can execute your notebooks and markdown files by passing the --execute flag to the start and build commands, i.e.:

bash
myst start --execute
myst build --execute

:::{note} Other flags can be relevant as well
For example if you want to produce a static HTML output from your content, you may wish to pass the --html flag as well:

bash
myst build --execute --html

:::

What content will be executed?

If you enable execution with the --execute flag as above, the following content will be executed at build time:

  • Cells in a Jupyter Notebook (.ipynb). These will be executed in the order they appeared in a notebook.
  • Markdown code cells with the code-cell directive. These will be executed similar to a code cell. See for more information.
  • Inline expressions with the eval role. These can be used to insert the outputs of a computation in-line with other text.

:::{note} Jupyter is required for execution
In order to execute your MyST content, you must install a Jupyter Server and the kernel needed to execute your code (e.g., the IPython kernel, the Xeus Python kernel, or the IRKernel.)
:::

Limit simultaneous executions

By default, up to {math}N-1 executable files are run concurrently, where {math}N is the number of available CPUs.

You can change this by using the --execute-parallel <n> option in your build command, where <n> sets the maximum number of executable documents that can run at the same time. This option is useful when your project includes executable content that is resource intensive or starts multiple subprocesses that might interfere with one another when run in parallel.

For example, setting --execute-parallel 2 will execute the documents two at a time.

To disable parallel execution: set --execute-parallel 1

Show raw Python objects like modules and classes

By default, MyST will suppress outputs from cells that return raw Python objects - like modules and classes - that don't have a string representation. For example with regular Python, you would observe this:

{code} python
:filename: Input
import math
math
{code} python
:filename: Output
<module 'math' from '/some/path/math.cpython-312-darwin.so'>

But within MyST, this output is suppressed by default. This also applies to results like matplotlib's Axes objects, pandas' GroupBy objects, etc..
If you'd like to instead show these outputs, see .

Allow a code-cell to error without failing the build

By default, MyST will stop executing a notebook if a cell raises an error.
If instead you'd like MyST to continue executing subsequent cells (e.g., in order to demonstrate an expected error message), add the raises-exception tag to the cell. If a cell with this tag raises an error, then the error is provided with the cell output, and MyST will continue executing the rest of the cells in a notebook.

:::{tip} How to manage cell tags
:class: dropdown

See notebook cell tags for how to manage cell tags
For example, in a {myst:directive}code-cell directive, you would add the raises-exception tag as follows:

markdown
```{code-cell}
:tags: [raises-exception]

print("Hello" + 10001)
```

:::

Skip particular code cells

Sometimes, you might have a notebook containing code that you don't want to execute. For example, you might have code-cells that prompt the user for input, which should be skipped during a website build. MyST understands the same skip-execution cell-tag that other Jupyter Notebook tools (such as Jupyter Book V1) use to prevent a cell from being executed.

:::{tip} Example of skipping a code cell
:class: dropdown
For example, in a {myst:directive}code-cell directive, the skip-execution tag can be added as follows:

markdown
```{code-cell}
:tags: [skip-execution]

name = input("What is your name?")
```

Here again, see notebook cell tags for more details on how to manage cell tags

:::

Additional cell tags, to hide or remove cells inputs or outputs, are also available.

Skip entire notebooks

You may wish to disable execution for certain notebooks. This can be done by setting the execute.skip frontmatter option to true, e.g.

markdown
---
kernelspec:
  name: python3
  display_name: Python 3

execute:
  skip: true
---

```{code-cell}
print("This will never be executed!")
```

See also notebook tags for how to set notebook-level metadata on a Jupyter Notebook.

Cache execution outputs

When MyST executes your notebook, it will store the outputs in a cache in a folder called execute/ in your MyST build folder.
On subsequent builds, MyST will re-use this cache rather than re-execute.

If you change the computational content of a notebook or a markdown page (ie, code in a code cell, or in an inline expression), then this cache will be reset and the code will be re-executed at the next build.

Force execution by deleting the cache

If you'd like to force re-execution of all the code in your MyST documents, use the following command:

bash
myst clean --execute

Alternatively, you can manually delete the execute/ folder in your build folder, e.g.:

bash
rm -rf _build/execute

(install-jupyter-server)=

Install Jupyter Server

MyST uses a Jupyter Server to execute your code.
Jupyter Server is distributed as a Python package, which can be installed from PyPI or conda-forge, e.g.

bash
pip install jupyter-server

Jupyter Server is only responsible for orchestrating execution of your code. To actually perform execution, you must also install a kernel. For Python, this might be ipykernel, e.g.

bash
pip install ipykernel

If Jupyter Server is installed and the --execute flag is passed to myst start or myst build MyST will attempt to launch a Jupyter Server using python -m jupyter_server.

Manually launch a Jupyter server

You can manually launch a Jupyter server and instruct MyST to use it for computation (rather than having MyST start its own Jupyter server).
This gives you more control over the process that executes your content, including specifying Jupyter servers that exist on non-local hardware (e.g. running in the cloud).

To manually specify a server, you must set two variables:

  • JUPYTER_BASE_URL: a URL where the server can be found. On a local machine, this is by default http://localhost:8888.
  • JUPYTER_TOKEN: the token that allows access to the Jupyter server.

For example, the following code sets these variables, then starts a Jupyter server with them so that MyST will use them to execute code:

bash
# Set the port for our local Jupyter process
port="8888"

# Define environment variables that will be used by MyST
# We'll use the values of these variables in our Jupyter server as well.
export JUPYTER_BASE_URL="http://localhost:${port}"
export JUPYTER_TOKEN="my-jupyter-token"

# Start the Jupyter server re-using the variables above
jupyter server --IdentityProvider.token="${JUPYTER_TOKEN}" --ServerApp.port="${port}" &

# Run the MyST build
# It will use the JUPYTER_* variables above to look for the server.
myst build --execute

# Stop the Jupyter server!
kill %1

Exercises (`docs/exercises.md`)


title: Exercises and Solutions short_title: Exercises thumbnail: ./thumbnails/exercise.png

There are two directives available to add exercises and solutions to your documents: (1) an exercise directive; and (2) a solution directive.
The exercises are enumerated by default and can take in an optional title argument as well as be "gated" around Jupyter Notebook cells.

Exercise Directive

::::{tab-set}
:::{tab-item} Example

{exercise}
:label: my-exercise

Recall that $n!$ is read as "$n$ factorial" and defined as
$n! = n \times (n - 1) \times \cdots \times 2 \times 1$.

There are functions to compute this in various modules, but let's
write our own version as an exercise.

In particular, write a function `factorial` such that `factorial(n)` returns $n!$
for any positive integer $n$.

:::
:::{tab-item} MyST Syntax

markdown
```{exercise}
:label: my-exercise

Recall that $n!$ is read as "$n$ factorial" and defined as
$n! = n \times (n - 1) \times \cdots \times 2 \times 1$.

There are functions to compute this in various modules, but let's
write our own version as an exercise.

In particular, write a function `factorial` such that `factorial(n)` returns $n!$
for any positive integer $n$.
```

:::
::::

Source: QuantEcon

The following options for exercise directives are supported:

:::{myst:directive} exercise
:::

Solution Directive

A solution directive can be included using the solution pattern.
It takes in the label of the directive it wants to link to as a required argument.
Unlike the exercise directive, the solution directive is not enumerable as it inherits numbering directly from the linked exercise.
The argument for a solution is the label of the linked exercise, which is required.

::::{tab-set}
:::{tab-item} Example

{solution} my-exercise
:label: my-solution

Here's one solution.

```{code-block} python
def factorial(n):
    k = 1
    for i in range(n):
        k = k * (i + 1)
    return k

factorial(4)
```

:::
:::{tab-item} MyST Syntax

markdown
````{solution} my-exercise
:label: my-solution

Here's one solution.

```{code-block} python
def factorial(n):
    k = 1
    for i in range(n):
        k = k * (i + 1)
    return k

factorial(4)
```
````

:::
::::

Source: QuantEcon

The following options for solution directives are supported:

:::{myst:directive} solution
:::

Referencing Exercises & Solutions

You can refer to an exercise using the standard link syntax:

  • [](#my-exercise), creates
  • [{name}](#nfactorial)[^note] creates {name}
  • [{number}](#my-exercise) creates {number}
  • [See Exercise](#my-exercise) creates See Exercise

:::{tip} Compatibility with Sphinx Exercise
:class: dropdown

You can also refer to an exercise using the {myst:role}ref role like {ref}`my-exercise`, which will display the title of the exercise directive. In the event that directive does not have a title, the title will be the default "Exercise" or "Exercise {number}" like so: {ref}my-exercise.

Enumerable directives can also be referenced through the {myst:role}numref role like {numref}`my-exercise`, which will display the number of the exercise directive. Referencing the above directive will display {numref}my-exercise. In this case it displays the same result as the {myst:role}ref role as exercise notes are (by default) enumerated.

Furthermore, numref can take in three additional placeholders for more customized titles:

  1. %s
  2. {number} which get replaced by the exercise number, and
  3. {name} by the exercise title.[^note]

For example,
{numref}`My custom {number} and {name} <my-exercise-label>`.

[^note]: If the exercise directive does not have a title, the label will be used instead.

:::

Referencing Solutions

You can refer to a solution directly as well using a Markdown link or using the {myst:role}ref role like: {ref}`my-solution` the output of which depends on the attributes of the linked directive.
If the linked directive is enumerable, the role will replace the solution reference with the linked directive type and its appropriate number like so: {ref}my-solution.

In the event that the directive being referenced is unenumerable, the reference will display its title: {ref}nfactorial-solution.

:::{note} Named Exercise & Solution
:class: dropdown simple
:icon: false

{exercise} $n!$ factorial
:label: nfactorial
:enumerated: false

Write a function `factorial` such that `factorial(int n)` returns $n!$
for any positive integer $n$.
{solution} nfactorial
:label: nfactorial-solution

Here's a solution in Java.

```{code-block} java
static int factorial(int n){
    if (n == 0)
        return 1;
    else {
        return(n * factorial(n-1));
    }
}

:::

If the title of the linked directive being reference does not exist, it will default to {ref}nfactorial-notitle-solution.

:::{note} Unnumbered Exercise & Solution
:class: dropdown simple
:icon: false

{exercise}
:label: nfactorial-notitle
:enumerated: false

Write a function `factorial` such that `factorial(int n)` returns $n!$
for any positive integer $n$.
{solution} nfactorial-notitle
:label: nfactorial-notitle-solution

Here's a solution in Java.

```{code-block} java
static int factorial(int n){
    if (n == 0)
        return 1;
    else {
        return(n * factorial(n-1));
    }
}

:::

Alternative Gated Syntax

To be able to be viewed as Jupyter Notebooks (e.g. in JupyterLab MyST) code-cell directives must be at the root level of the document for them to be executed.
This maintains direct compatibility with the jupyter notebook and enables tools like jupytext to convert between myst and ipynb files.

As a result executable code-cell directives cannot be nested inside of exercises or solution directives.

The solution to this is to use the gated syntax.

{note}
This syntax can also be a convenient way of surrounding blocks of text that may include other directives that you wish
to include in an exercise or solution admonition.

Basic Syntax
::::{tab-set}
:::{tab-item} Example

{exercise-start}
:label: ex1
python
# Some setup code that needs executing

and maybe you wish to add a figure

{figure} https://github.com/rowanc1/pics/blob/main/sunset.png
{exercise-end}

:::

:::{tab-item} MyST Syntax

markdown
```{exercise-start}
:label: ex1
```

```python
# Some code to explain the figure
```

and maybe you wish to add a figure

```{figure} https://github.com/rowanc1/pics/blob/main/beach.png

```

```{exercise-end}

```

:::
::::

This can also be completed for solutions with solution-start and solution-end directives.
The solution-start and exercise-start directives have the same options as original directive.

{warning} mismatched start & end
:class: dropdown
If there are missing `-start` and `-end` directives, this will cause an extension error,
alongside feedback to diagnose the issue in document structure.

Hiding Directive Content

To visually hide the content, simply add :class: dropdown as a directive option, similar to an admonition.

::::{tab-set}
:::{tab-item} Example

{exercise}
:class: dropdown

Recall that $n!$ is read as "$n$ factorial" and defined as
$n! = n \times (n - 1) \times \cdots \times 2 \times 1$.

There are functions to compute this in various modules, but let's
write our own version as an exercise.

In particular, write a function `factorial` such that `factorial(n)` returns $n!$
for any positive integer $n$.

:::

:::{tab-item} MyST Syntax

markdown
```{exercise}
:class: dropdown

Recall that $n!$ is read as "$n$ factorial" and defined as
$n! = n \times (n - 1) \times \cdots \times 2 \times 1$.

There are functions to compute this in various modules, but let's
write our own version as an exercise.

In particular, write a function `factorial` such that `factorial(n)` returns $n!$
for any positive integer $n$.
```

:::
::::

Remove Directives

Any specific directive can be hidden by introducing the :hidden: option. For example, the following example will not be displayed

{myst}
```{exercise}
:hidden:

This is a hidden exercise directive.
```

:::{warning} Exercises are not supported in some PDF outputs
You'll encounter warnings when exporting exercises and solutions to pdf using either LaTeX or Typst.
This is because these directives are not natively supported in these formats.
See this plugin to enable exporting these directives to pdf formats - including numeration.
:::

% TODO: Remove All Solutions
% TODO: Custom CSS


External References (`docs/external-references.md`)


title: External References thumbnail: ./thumbnails/external-references.png

MyST Markdown allows you to connect your wiki:documents to external linked content like wiki:Wikipedia, which allow for hover-references with external content.
External references are references to structured content or documents that are outside of your project.
MyST supports referencing rich content in a growing number of formats, including:

  1. other mystmd projects, with rich cross-linking of content
  2. integrating directly with Wikipedia articles to show tooltips,
  3. linking to other Sphinx documentation using intersphinx,
  4. link to files on GitHub and show inline previews,
  5. showing structured content from scholarly sources like DOIs or RRIDs.
{seealso}
[](./cross-references.md) for referencing content in your project and [](./citations.md) to cite scholarly work and create bibliographies.

(myst-xref)=

External MyST projects

When using the HTML renderer for MyST, an API is provided for the deployed site.
This provides pre-parsed, structured content as an AST that can be included in other projects and rendered in a tooltip.

:::{tip} Add .json to any MyST URL to access the structured data
All MyST pages come with a structured data representation that provides authors, license information,
as well as the full content in a parsed form that can be used for an inline reference on external pages.
Try adding .json at the end of the URL on this page.
:::

In your project configuration, include the references object with links out to the external MyST projects that you will reference in your project. The keys in this object have no special meaning; they are just used within your project to look up the external project. The example below shows how you would load cross-references that are pulled from the MyST Specification documentation.

{code-block} yaml
:label: myst-xref-config
:filename: myst.yml
:caption: Add `references` to your project configuration in your `myst.yml`, MyST will then provide access to all of content on those pages in links and embeds.
project:
  references:
    spec: https://mystmd.org/spec

When you specify these in your project configuration, MyST will load the cross-references and provide access to all of the pages and reference targets in that project.
References are cached to disk locally in the _build folder, eliminating duplicate web requests on subsequent builds.
To delete the cache and manually re-load the references, run myst clean --cache.

{important}
# MyST Cross Reference Examples

```{list-table}
:label: tbl:syntax-xref
:header-rows: 1
* - MyST Syntax
  - Rendered
* - `[](xref:spec)`
    : A reference to the first page of the MyST Spec.
  - [](xref:spec)
* - `[](xref:spec#paragraph)`
    : A reference to the node "paragraph" target in the MyST Spec.
  - [](xref:spec#paragraph)
* - `<xref:spec/tables#example>`
    : Alternate link syntax that references a heading on a specific page of the spec documentation.
  - <xref:spec/tables#example>
```

To reference content in the linked MyST project, use the xref: protocol in a link, followed by the corresponding external project key you defined under references, the url path and the target.
For example, <xref:spec/tables#example> renders to:
"xref:spec/tables#example"
and is made of a protocol ("xref"), external project key ("spec"), path ("/tables") and target ("#example").

protocol
: The protocol for this type of link is xref:, and is what selects for cross-project referencing.

key
: the external project key above is "spec" which is defined in your local project configuration above.

path
: the path is everything that follows the external project key before the #. It corresponds directly to the URL path in the MyST site. In the example above, path is /tables.
: Path is optional in most cases; the target can be resolved against the entire project without the path. An exception to this is headings without an explicit label in the source markdown — these will require path. Note that these references may not be intended to be persistent by the source author, so use caution in linking to them!

target
: The target is everything that follows the # and is a named reference in the project. In the example above it is "syntax".
: Target is also optional - if not provided, the cross reference will simply link to a page in the external MyST project. However, without the target, there will not be a rendered tooltip.
: When creating a cross reference, you can determine path and target by simply navigating to your target content in the external MyST project and copying the path and fragment from the URL.

If no link text is provided, e.g. [](xref:...), text will be generated from the external project at build-time. You may override this behavior by providing you own text, [text](xref:...).

:::{tip} References are stored in a myst.xref.json file
:class: dropdown
All MyST sites published to the web expose a myst.xref.json file that contains all of the cross-references information about a MyST site.
This file is what MyST pulls when you point to an external MyST site for cross-referencing.
It is also a machine-readable record that can be used for analyzing the cross-referencing behavior of MyST projects.
:::

(intersphinx)=

Sphinx documentation

MyST can integrate directly with other Sphinx documentation, which is used in many Python projects including the standard library.
This re-uses the reference specification defined by the intersphinx plugin for Sphinx.

% TODO: Update this when we release Jupyter Book V2 because /stable will no longer be a Sphinx site.

Similar to MyST cross references, use the references object to list Sphinx projects. For example, in the demonstration below we will load the Python 3.13 documentation and Jupyter Book V1 docs, both of which use sphinx and expose cross references through an objects.inv file.

(intersphinx-config)=

yaml
references:
  python: https://docs.python.org/3.13/
  jupyterbook: https://jupyterbook.org/en/stable/
{tip}
In your `references` object, you may freely mix MyST and Sphinx projects. MyST will decipher which to use, based on the presence of `myst.xref.json` or `objects.inv` files at the specified URL.

The behavior of these entries is identical to MyST cross references: the remote objects.inv file, which contains all available project references, is downloaded and cached in the _build folder.

{important}
# Sphinx Examples

```{list-table}
:header-rows: 1
* - MyST Syntax
  - Rendered
* - `[](xref:python#zipapp-specifying-the-interpreter)`
    : A reference to the reference documentation in Python.
  - [](xref:python#zipapp-specifying-the-interpreter)
* - `[](xref:jupyterbook#content:references)`
    : A reference to the Jupyter Book documentation, that brings you directly to the reference,
      as well as fills in the label text.
  - [](xref:jupyterbook#content:references)
```

To reference a function, class or label in the linked documentation, use the xref: protocol in a link followed by the project key and the target.
For example, <xref:python#library/abc> renders to:
"xref:python#library/abc"
and is made of a protocol ("xref"), key ("python") and target ("#library/abc").

protocol
: The protocol for this type of link is xref:, and is what selects for cross-project referencing.

key
: the external project key above is "python" which is defined in your local project configuration above.

target
: The target is everything that follows the # and is a named reference in the project.
: In the example above it is "library/abc".

As with any link, the text can be overridden using markdown link syntax [text](xref:...).

{tip}
:class: dropdown
# How to find the Sphinx target?

The HTML IDs that are part of the documentation are not always the targets that are used in the documentation. The easiest way to find the target to use is to look at the source documentation in RST or MyST.

Look for the `(target)=` syntax or `:label:` on a directive.

MyST will warn you in the console if your target is not found.

You can also use the [intersphinx](https://www.npmjs.com/package/intersphinx) package, for example, `list`, or `parse` an intersphinx inventory:

```bash
>> intersphinx list https://docs.python.org/3.13 --domain std:doc --includes abc --limit 5

std:doc Abstract Base Classes (library/abc)
  https://docs.python.org/3.13/library/abc.html
std:doc Abstract Base Classes for Containers (library/collections.abc)
  https://docs.python.org/3.13/library/collections.abc.html
```

Use the target in the parenthesis, which would be `xref:python#library/abc` above.

MyST Markdown can directly integrate with wiki:Wikipedia to create hover-card information directly integrated into your myst documents. The syntax follows standard markdown links, under the wiki: protocol followed by the page title[^1]. As with any other link, you can either follow a [text](wiki:Page_Title) or <wiki:Page_Title>, which if no text is provided for the links will be replaced with the page title.

[^1]: Replace any spaces in the page title with underscores.

{myst}
Primordial <wiki:gravitational_waves> are hypothesized to arise from <wiki:cosmic_inflation>, a faster-than-light expansion just after the <wiki:big_bang>.

The links will take you to Wikipedia, as well as provide a tooltip and description directly on the page.

To show different text you can use a similar technique to references:
[my **bold** text](wiki:reference)

{myst}
* [big bang](wiki:The_Big_Bang_Theory)
* [](wiki:big_bang)
{tip}
:class: dropdown
# Finding and formatting the page title
To find the page title, browse Wikipedia and copy the last part of the URL, for example:\
`Page_Title` in `https://wikipedia.org/wiki/Page_Title`. If you do not supply text for the link specifically, then the case of the link will be preserved and shown without the underscores.

Usually the page titles resolve properly, so just try guessing when you are writing and then you can check them with the live hover preview.

Note that if the page title has spaces in it, simply replace them with underscores.
{important}
:class: dropdown
# Different languages or wikis

There are many different official and unofficial wikis that use the same [Wikimedia](wiki:Wikimedia_Foundation) technology, including subdomains in various languages.

Wikipedia links, like `https://fr.wikipedia.org/wiki/Croissant_(viennoiserie)` will work fine out of the box, and point to [](https://fr.wikipedia.org/wiki/Croissant_(viennoiserie)) with the popup still working!

% TODO: Allow the ability to set the default language to use on your MyST site. This is already possible in the transformers.

% TODO: Set the default wiki links, or an additional wiki link in references, e.g. to something like https://wiki.seg.org/wiki/Knowledge_tree

GitHub

Issues and Pull Requests

MyST Markdown can directly link and show preview of GitHub issues and Pull Requests, for example, #336 and #87. To enable this, just use a normal link to your

markdown
[#87](https://github.com/jupyter-book/myst-theme/pull/87)

If you do not include children for the link, then the default text will become owner/repo#123.

Linking to Code

MyST Markdown can directly integrate with links to GitHub to create hover-card information directly integrated into your MyST documents. For example, a link to the linkTransforms plugin code shows a preview of the code. The code preview works for both multiple line numbers and highlighting single lines, which shows the surrounding ten lines, with the referenced line highlighted. If you reference the full file then the first ten lines of the file are shown in the preview.

{important}
:class: dropdown
# Creating GitHub links to code
GitHub links to code can be generated on the GitHub web application when browsing code and click on the line numbers, then copy the URL. To select multiple lines, click your first line then shift-click to select multiple lines, the URL will be updated to end with `#L4-L6`. The structure of the link should look like:

```text
https://github.com/{{org}}/{{repo}}/blob/{{reference}}/file.py#L4-L6
```

Any git `reference` will work, however, picking a branch like `main` may mean that your code line numbers will change, instead, you may want to go to navigate to a specific git commit or tag, which will show up in the URL.

% TODO: Add ability to reference issues and PRs with queries for the issue names, and PR information.

Linking DOIs

It is possible to include DOIs as external content, and they are also added as citations to your project and show up in the references section at the bottom of a document. See for more details, specifically , which explains linking DOIs with the <doi:10.5281/zenodo.6476040> or [](doi:10.5281/zenodo.6476040) to create a citation, for example (doi:10.5281/zenodo.6476040).

Research Resource Identifiers

{abbr}RRID (Research Resource Identifiers)s are persistent, unique identifiers for referencing a research resource, such as an antibody, plasmid, organism, or scientific tool. These are helpful for ensuring reproducibility and exact communication in scientific studies. See the RRID website for more information.

MyST Markdown allows you to directly integrate with the RRID database to pull information and validate the links are correct as you are writing documents. The metadata is passed to subsequent systems (e.g. PDF documents, compatible journals and preprint servers) and helps keep your science reproducible.

To create an RRID link, use the rrid: protocol followed by the resource identifier, for example:

{note}
# Example Methods Section

An edited excerpt from _Impaired speed encoding and grid cell periodicity in a mouse model of tauopathy_
{cite:p}`10.7554/eLife.59045`. The authors used Matlab (<rrid:SCR_001622>) in their analysis.

Click on one of the RRIDs below to see additional metadata!

```{list-table}
:header-rows: 1

* - Resource type
  - Designation
  - Source
  - Identifiers

* - Strain (M. musculus)
  - rTg4510, Tg(Camk2a-tTA)
  - ENVIGO
  - <rrid:MGI:4819951>

* - Software, algorithm
  - Klusta suite
  - {cite}`10.1038/nn.4268`
  - <rrid:SCR_014480>

* - Software, algorithm
  - MATLAB R2019b
  - [Mathworks](https://uk.mathworks.com/)
  - <rrid:SCR_001622>\
    <rrid:SCR_005547>

* - Software, algorithm
  - OriginPro 2019b
  - [OriginLab](https://www.originlab.com/)
  - <rrid:SCR_014212>
```

Research Organization Registry

The Research Organization Registry (ROR) is a global, community-led registry of open persistent identifiers for research organizations. You can add these to your MyST frontmatter or use the links directly in your documents.

To create an ROR link, use the ror: protocol followed by the identifier, for example:

  • [](ror:03rmrcq20) becomes
  • <ror:03rmrcq20> becomes ror:03rmrcq20

You may also use a URL similar to https://ror.org/03rmrcq20. To find your organization use the search provided at ror.org


Figures (`docs/figures.md`)


title: Images, Figures and Videos short_title: Images, Figures, & Videos thumbnail: ./thumbnails/figures.png

MyST Markdown can be used to include images and figures in your documents as well as referencing those images easily throughout your website, article or paper.

Simple images

The simplest way to include an image is to use the standard Markdown syntax:

md

You can explore a demo of images in the discussion of features of MyST.

Using standard markdown to create an image will render across all output formats (HTML, TeX, Word, PDF, etc).
However, this markdown syntax is limited in the configuration that can be applied beyond alt text and an optional title.

There are two directives that can be used to add additional information about the layout and metadata associated with an image.
For example, {myst:directive}image.width, {myst:directive}alignment or a {myst:directive}figure caption <figure.body>.

image
: The {myst:directive}image directive allows you to customize {myst:directive}image.width, {myst:directive}alignment , and other {myst:directive}classes to add to the image.

figure
: The {myst:directive}figure directive can contain a {myst:directive}figure caption <figure.body> and allows you to cross-reference this in other parts of your document.

(image-directive)=

Image directive

The {image} directive lets you include a simple image on your page.
You can control the alt-text, style, etc.
Here's an example:

{myst}
```{image} https://github.com/rowanc1/pics/blob/main/grapes-wide.png?raw=true
:alt: Grapes on a vineyard
:width: 500px
:align: center
```

(figure-directive)=

Figure directive

Figures can wrap one-or-more images.
They'll be numbered, so referencing them creates links like Figure 1.

{myst}
```{figure} https://github.com/rowanc1/pics/blob/main/sunset.png?raw=true
:label: myFigure
:alt: Sunset at the beach
:align: center

Relaxing at the beach 🏝 🌊 😎
```
{note}
You may also embed [notebook cell outputs as images or figures](#targeting-cells).

Subfigures

Subfigures are created by putting one or more images in the directive body instead of as an argument.
These will be numbered as Figure 1a and Figure 1b, etc. For example:

:::{figure}
:label: subFigure
:align: left

We saw some great things on our trips this year to Banff, Canada 🇨🇦 and San Francisco, USA 🌉.
:::

You can cross-reference either the whole figure [@subFigure], or an individual subfigure [@subFigure-a] or [@subFigure-b].
Each subfigure is given an implicit reference that matches the figure label with a suffix of their letter. For example, let's say you have a parent figure and give it a label my-figure, and it has two sub-figures. Each subfigure can be referred to as #my-figure-a and #my-figure-b, respectively.

If you provide a label for a specific subfigure, that label will be used instead of the implicit label.

{myst}
:::{figure}
:label: my-figure
:align: left

(my-figure-fruit)=




Some pictures of fruit and the ocean!
:::

See [](#my-figure-fruit) for the fruit, and [](#my-figure) to reference both subfigures.

When referring to subfigures, the {number} that is used includes the parent enumerator by default (that is: 1a rather than just a).
To use the sub-enumerator only, you can use the syntax {subEnumerator} in your text link which will be replaced with the sub-enumerator (that is: a rather than 1a).

CSS classes can be applied to make subfigures appear side-by-side or in a grid layout.
:class: grid grid-cols-2 items-end gap-4
can be used to create a two-column grid of images with aligned bottoms and a gap between them.

::::{figure}
:class: grid grid-cols-2 items-end gap-4
:label: fig_combined2
:width: 100%

Some lovely pictures
::::

Control sub-figure layout with a grid

For responsive layouts, wrap the content in a {grid} directive.
In this case, the figure has a single entry, representing the grid of images that you provide.
It will not create implicit references for sub-figures as desecribed above.
The example below uses one column on narrow screens and two on wide ones (1 1 2 2).

::::{figure}
:label: fig-grid

:::{grid} 1 1 2 2

:::

Pictures of fruit and the ocean.
::::

Supported Image Formats

MyST supports many images formats including .png, .jpg, .gif, .tiff, .svg, .pdf, and .eps.
Many of these image formats are easily supported for HTML themes including .png, .jpg and .gif.
However, the raster image formats can be further optimized to improve site performance, MyST translates these to the modern .webp format while the site is building.
The original file-format is also included your site, with a srcset and fallback for older web-browsers.

{tab-set}
````{tab-item} PNG
:::{figure} ./images/myst-image.png
:width: 50%
`.png` is natively supported in all exports. The image is converted to `.webp` for web-browsers.
:::
````

````{tab-item} JPG
:::{figure} ./images/myst-image.jpg
:width: 50%
`.jpg` or `.jpeg` is natively supported in all exports. The image is converted to `.webp` for web-browsers.
:::
````

````{tab-item} GIF
:::{figure} ./images/myst-image.gif
:width: 50%
`.gif` is supported web-browsers and Microsoft Word, the first frame is extracted for $\LaTeX$ and PDF exports. The image is converted to `.webp` for web-browsers.
:::
````

````{tab-item} TIFF
:::{figure} ./images/myst-image.tiff
:width: 50%
`.tiff` is not supported by most web-browsers, and is converted to `.png`. Microsoft Word, $\LaTeX$ and PDF exports can work with these `.png` images, which are also converted to `.webp` for web-browsers.
:::
````

````{tab-item} SVG
:::{figure} ./images/myst-image.svg
:width: 50%
`.svg` is supported by web-browsers and is not further optimized or rasterized. When exporting to $\LaTeX$ and PDF the images are translated to `.pdf` using `inkscape` or as a fallback to `.png` using `imagemagick`. Microsoft Word requires the `.png` export.
:::
````

````{tab-item} PDF
:::{figure} ./images/myst-image.pdf
:width: 50%
A `.pdf` image is not supported by web-browsers or Microsoft Word. The images are translated to `.png` using `imagemagick`. $\LaTeX$ and PDF use the `.pdf` image directly.
:::
````

````{tab-item} EPS
:::{figure} ./images/myst-image.eps
:width: 50%
An `.eps` image is not supported by web-browsers or Microsoft Word. The images are translated to `.png` using `imagemagick`. $\LaTeX$ and PDF use the `.eps` image directly.
:::
````

Image Transformers

There are formats that are not supported by web-browsers but are common in scientific writing like .tiff, .pdf and .eps.
For site builds, these are converted to .svg or .png as appropriate and available.
For export to $\LaTeX$, PDF or Microsoft Word, the files are converted to an appropriate format that the export can handle (e.g. $\LaTeX$ can work directly with .pdf images).
For animated images, .gif, the first frame is extracted for static exports.

:::{tip} Installing Image Converters
:class: dropdown
The image transforms and optimizations requires you to have the following packages installed:

  • ImageMagick for conversion between raster formats
  • inkscape for conversion between some vector formats
  • webp for image optimizations

:::

(figures:multiple-images)=

Multiple Images

If you have manually converted your images or have different images for different formats, use an asterisk (*) as the extension.
All images matching the provided pattern will be found and the best image available will be used for the export:

text

For example, when exporting to $\LaTeX$ the best format is a .pdf (if available); in a web export, a .webp or .svg will be chosen before a .png.
In all cases, if an appropriate format is not available the image will be converted.

Videos

To embed a video you can either use a video platforms embed script or directly embed an mp4 video file. For example:

markdown
:::{figure} ./videos/links.mp4
An embedded video with a caption!
:::

or

markdown

will copy the video to your static files and embed a video in your HTML output.

:::{figure} ./videos/links.mp4
An embedded video with a caption!
:::

If you have ffmpeg installed, you may also include .mov and .avi video files, and MyST will convert them to .mp4 and include them.
Videos can also be used in the image or even in simple Markdown image.

Use an image in place of a video for static exports

If you'd like an image to display for static exports (like PDFs), use the asterisk (*) wildcard matching described in .

For example, if you had the following two files:

text
myvideo.mp4  <-- A video of something
myvideo.png <-- A frame of the video as an image

Then you could link them both with:

md

When you build an HTML output, the video will be used, and when you build a PDF output, the image will be used.

YouTube Videos

If your video is on a platform like YouTube or Vimeo, you can use the {myst:directive}iframe directive that takes the URL of the video.

{myst}
:::{iframe} https://www.youtube.com/embed/F3st8X0L1Ys
:width: 100%
Get up and running with MyST in Jupyter!
:::

You can find this URL when clicking share > embed on various platforms. You can also give the {myst:directive}iframe directive {myst:directive}iframe.width, {myst:directive}iframe.title (for accessibility), and a {myst:directive}caption .

Provide Light and Dark Mode images

To provide images for light and dark mode, use Tailwind CSS classes for light and dark mode.


Frontmatter (`docs/frontmatter.md`)

Content frontmatter options

The following table lists the available frontmatter fields. If a field has sub-fields, see the relevant description on the page below.

Where to put these fields

Placement depends on the Field Behavior column below:

  • page & project: valid in both page frontmatter and project in myst.yml
  • page only: valid only in page frontmatter
  • project only: valid only under project in myst.yml
  • page can override project: valid in both, with page values taking precedence

In page frontmatter: fields are top-level. For example:

{code-block} md
:filename: page.md
---
edit_url: null
---

In project-wide configuration: fields are nested under the project key. For example:

{code-block} yaml
:filename: myst.yml
project:
  edit_url: null

All available frontmatter fields

text
/* Detailed source-code truncated for AI context efficiency. */

+++

(titles)=

Titles

There are several fields to title MyST projects and pages. Primary page and project titles can be specified simply as title. Pages and projects also both have short_title; this should provide a summarized title in less than 40 characters. It is used where space is limited, for example a site navigation panel, running-head titles in an static export, etc. You may specify subtitle; this conveys complementary information to the title and may be displayed below the title.

{note} defining page title in markdown
If `title` is not defined in the frontmatter, it will be pulled from the a heading at the top of the markdown instead. In this case, the heading will be removed from the content to the frontmatter for usage in a MyST site header or exported document title page.

```markdown
---
author: Marissa Myst
---

# My MyST Title

## Introduction

For this page, "My MyST Title" is the title!
...
```

If removing the title causes unexpected problems with the page formatting, you may set `title: null` in the frontmatter to prevent heading removal. The first heading will _still_ be copied as a placeholder title, but it will not be removed from the markdown. However, in these cases, it is probably easiest to simply define a title in frontmatter!

+++

(thumbnail-and-banner)=

Thumbnail & Banner

The thumbnail is used in previews for your site in applications like Twitter, Slack, or any other link preview service. This should, by convention, be included in a thumbnails folder next to your content. You can also explicitly set this field to any other image on your local file system or a remote URL to an image. This image will get copied over to your public folder and optimized when you build your project.

yaml
thumbnail: thumbnails/myThumbnail.png

If you do not specify an image the first image in the content of a page will be selected. If you explicitly do not want an image, set thumbnail to null.

You can also set a banner image which will show up in certain themes, for example, the article-theme:

yaml
banner: banner.png

:::{figure} ./images/article-theme.png
:label: banner-example
Example of a banner in a site using the article-theme.
:::

(frontmatter:authors)=

Authors

The authors field is a list of author objects. Available fields in the author object are:

{list-table} frontmatter information for authors
:header-rows: 1
:label: table-frontmatter-authors
* - Field
  - Description
* - `name`
  - a string OR CSL-JSON author object - the author's full name; if a string, this will be parsed automatically. Otherwise, the object may contain `given`, `surname`, `non_dropping_particle`, `dropping_particle`, `suffix`, and full name `literal`
* - `id`
  - a string - a local identifier that can be used to reference a repeated author
* - `orcid`
  - a string - a valid ORCID identifier with or without the URL
* - `corresponding`
  - boolean (true/false) - flags any corresponding authors, you must include an `email` if true.
* - `email`
  - a string - email of the author. Only displayed in the rendered output when `corresponding` is `true`. See [](#author:email)
* - `roles`
  - a list of strings - must be valid [CRediT Contributor Roles](https://credit.niso.org/). See [](#credit-roles) for more information.

    ```yaml
    authors:
      - name: Penny Crediton
        roles:
          - Conceptualization
          - Data curation
          - Validation
    ```
/* Detailed source-code truncated for AI context efficiency. */
```yaml
    authors:
      - name: Marissa Myst
        affiliations:
          - id: ubc
            institution: University of British Columbia
            ror: 03rmrcq20
            department: Earth, Ocean and Atmospheric Sciences
      	  - ACME Inc
      - name: Miles Mysterson
        affiliation: ubc
    ```

    See [](#affiliations) for more information on how to concisely write affiliations.
* - `equal_contributor`
  - a boolean (true/false), indicates that the author is an equal contributor
* - `deceased`
  - a boolean (true/false), indicates that the author is deceased
* - `note`
  - a string, a freeform field to indicate additional information about the author, for example, acknowledgments or specific correspondence information.
* - `phone`
  - a phone number, e.g. `(301) 754-5766`
* - `fax`
  - for people who still use these machines, beep, boop, beeeep! 📠🎶
* - `url`, `github`, and other social links
  - See @table-frontmatter-social-links for all social profile links for the author

(social-links)=

Contributors and affiliations can also have social links and URLs.

{list-table} social links for contributors and affiliations.

Affiliations

Below are all the possible fields for frontmatter affiliations.

{list-table} frontmatter information for affiliations
:header-rows: 1
:label: table-frontmatter-affiliations
* - Field
  - Description
* - `id`
  - a string - a local identifier that can be used to reference a repeated affiliation
* - `name`
  - a string - the affiliation name. Either `name` or `institution` is required
* - `institution`
  - a string - Name of an institution or organization (for example, a university or corporation)

    If your research group has a name, you can use both `name` and `institution`,
    however, at least one of these is required.
* - `department`
  - a string - the affiliation department (e.g. Chemistry 🧪)
* - `doi`, `ror`, `isni`, `ringgold`
  - Identifiers for the affiliation (DOI, ROR, ISNI, and Ringgold).

    We suggest using https://ror.org if possible to search for your institution.

    ```yaml
    affiliations:
      - name: Boston University
        ringgold: 1846
        isni: 0000 0004 1936 7558
        ror: 05qwgg493
        doi: 10.13039/100018578
    ```
* - `email`
  - a string - email of the affiliation, required if `corresponding` is `true`
* - `address`, `city`, `state`, `postal_code`, and `country`
  - affiliation address information. In place of `state` you can use `province` or `region`.
* - `url`, `github`, and other social links
  - See @table-frontmatter-social-links for all social profile links for the affiliation
* - `phone`
  - a phone number, e.g. `(301) 754-5766`
* - `fax`
  - A fax number for the affiliation
* - `collaboration`
  - a boolean - Indicate that this affiliation is a collaboration, for example, `"MyST Contributors"` can be both an affiliation and a listed author. This is used in certain templates as well as in [JATS](https://jats.nlm.nih.gov/archiving/tag-library/1.3/element/collab.html).

Date

The date field is a string and should conform to a well-defined calendar date.
It should be quoted most of the time for notebook frontmatter to avoid parsing issues on the Jupyter side.
Examples of acceptable date formats are:

  • "2022-12-14" - YYYY-MM-DD
  • "01 Jan 2000" - DD? MON YYYY
  • Sat, 1 Jan 2000 - DAY, DD? MON YYYY

These dates correspond to two main formats:

  • A strict (full, extended) calendar date defined by ISO 8601 (see also RFC 3339)
  • A date-only variant of RFC 2822, built using the RFC gammar rules.

(frontmatter:exports)=

Exports

Exports allow you to generate static versions of your MyST documents, often through intermediary build engines like Latex.
For usage information, see .

{list-table} frontmatter export definitions
:header-rows: 1
:label: table-frontmatter-exports
* - Field
  - Description
* - `id`
  - a string - a local identifier that can be used to reference the export
* - `format`
  - one of `pdf` (built with $\LaTeX$ or Typst, depending on the template), `tex` (raw $\LaTeX$ files), `pdf+tex` (both PDF and raw $\LaTeX$ files) `typst` (raw Typst files and built PDF file), `docx`, `md`, `jats`, or `meca`
* - `template`
  - a string - name of an existing [MyST template](https://github.com/myst-templates) or a local path to a template folder. Templates are only available for `pdf`, `tex`, `typst`, and `docx` formats.
* - `output`
  - a string - export output filename with a valid extension or destination folder
* - `zip`
  - a boolean - if `true`, zip the output - only applies for multi-file exports `tex`, `pdf+tex` and `typst`.
* - `articles`
  - a list of strings - path(s) to articles to include in your export - this is required for exports defined in project frontmatter; for page frontmatter, the default article will be the page itself. Not all exports currently support multiple articles.
* - `toc`
  - a string - path to jupyterbook `_toc.yml` file - may be used as an alternative to listing `articles`
* - `sub_articles`
  - a list of strings - path(s) to sub-articles for `jats` export

(frontmatter:downloads)=

Downloads

Downloads allow you to include downloadable files with a MyST website.
They are specified in either:

+++ {"label": "frontmatter-downloads" }

In project configuration:

{code-block} yaml
:filename: myst.yml
project:
  downloads:
    - file: ...
    - id: ...

In page frontmatter:

{code-block} yaml
:filename: page.md
---
downloads:
  - file: ...
  - id: ...
---

+++

See information about how to use this feature.
Below is a list of all possible downloads configuration.

{list-table} frontmatter download definitions
:header-rows: 1
:label: table-frontmatter-downloads
* - Field
  - Description
* - `id`
  - a string - reference to an existing `export` identifier. The referenced export may be defined in a different file. If `id` is defined, `file`/`url` are not allowed.
* - `file`
  - a string - a path to a local file. If `file` is defined, `id`/`url` are not allowed.
* - `url`
  - a string - either a full URL or a relative URL of a page in your MyST project. If `url` is defined, `id`/`file` are not allowed.
* - `title`
  - a string - title of the `downloads` entry. This will show up as text on the link in your MyST site. `title` is recommended for all downloads, but only required for `url` values; files will default to `filename` title
* - `filename`
  - a string - name of the file upon download. By default, this will match the original filename. `url` values do no require a `filename`; if provided, the `url` will be treated as a download link rather than page navigation.
* - `static`
  - a boolean - this is automatically set to `true` for local files and `false` otherwise. You may also explicitly set this to `false`; this will bypass any attempt to find the file locally and will keep the value for `url` exactly as it is provided.

(licenses)=

Licenses

This field can be set to a string value directly or to a License object.

Available fields in the License object are content and code allowing licenses to be set separately for these two forms of content, as often different subsets of licenses are applicable to each. If you only wish to apply a single license to your page or project use the string form rather than an object.

If selecting a license from the SPDX License List, you may simply use the "Identifier" string; MyST will expand these identifiers into objects with name, url, and additional metadata related to open access (OSI-approved, FSF free, and CC). Identifiers for well-known licenses are easily recognizable (e.g. MIT or BSD) and MyST will attempt to infer the specific identifier if an ambiguous license is specified (e.g. GPL will be interpreted as GPL-3.0+ and a warning raised letting you know of this interpretation). Some common licenses are:

{list-table}
:header-rows: 1
:label: table-common-licenses

* - Common Content Licenses
  - Common Code Licenses

* - - `CC-BY-4.0`
    - `CC-BY-SA-4.0`
    - `CC-BY-NC-SA-4.0`
    - `CC0-1.0`

  - - `MIT`
    - `BSD`
    - `GPL-3.0+`
    - `Apache-2.0`
    - `LGPL-3.0-or-later`
    - `AGPL`

By using the correct SPDX Identifier, your website will automatically use the appropriate icon for the license and link to the license definition. The simplest and most common example is something like:

yaml
license: CC-BY-4.0

Nonstandard licenses

Although not recommended, you may specify nonstandard licenses not found on the SPDX License List. For these, you may provide an object where available fields are id, name, url, and note. You can also extend the default SPDX Licenses by providing modified values for these fields. Here is a more complex example where content and code have different licenses; content uses an SPDX License with an additional note, and code uses a totally custom license.

yaml
license:
  content:
    id: CC-BY-4.0
    note: When attributing this content, please indicate the Source was MyST Documentation.
  code:
    name: I Am Not A Lawyer License
    url: https://example.com/i-am-not-a-lawyer

(frontmatter:funding)=

Funding

Funding frontmatter is able to contain multiple funding and open access statements, as well as award info.

It may be as simple as a single funding statement:

yaml
funding: This work was supported by University.

Funding may also specify award id, name, sources (affiliation object or reference), investigators (contributor objects or references), and recipients (contributor objects or references).

yaml
authors:
  - id: auth0
    name: Jane Doe
funding:
  statement: This work was supported by University.
  id: award-id-000
  name: My Award
  sources:
    - name: University
  investigators:
    - name: John Doe
  recipients:
    - auth0

Multiple funding objects with multiple awards are also possible:

yaml
authors:
  - id: auth0
    name: Jane Doe
funding:
  - statement: This work was supported by University.
    awards:
      - id: award-id-000
        name: My First Award
        sources:
          - name: University
        investigators:
          - name: John Doe
        recipients:
          - auth0
      - id: award-id-001
        name: My Second Award
        sources:
          - name: University
        investigators:
          - name: John Doe
        recipients:
          - auth0
  - statement: Open access was supported by Consortium.
    open_access: Users are allowed to reproduce without prior permission
    awards:
      - id: open-award-999
        sources:
          - name: Consortium

Venue

The term venue is borrowed from the OpenAlex API definition:

Venues are where works are hosted.

For MyST frontmatter, the venue object holds metadata for journals and conferences where a work may be presented.

{list-table} available venue fields
:header-rows: 1
:label: table-frontmatter-venue
* - Field
  - Description
* - `title`
  - full title of the venue
* - `short_title`
  - short title of the venue; often journals have a standard abbreviation that should be defined here
* - `url`
  - URL of the venue
* - `doi`
  - the _venue_ DOI
* - `number`
  - number of the venue in a series, for example the "18th Python in Science Conference"
* - `location`
  - physical location of a conference
* - `date`
  - date associated with the venue, for example the dates of a conference. This field is a string, not a timestamp, so it may be a date range.
* - `series`
  - title of a series that this venue or work is part of. Examples include a conference proceedings series, where each year a new conference-specific proceedings journal is created, or a category of articles across multiple issues, such as colloquium papers.
* - `issn`
  - ISSN for the publication
* - `publisher`
  - publisher of the journal

Some typical venue values may be:

yaml
venue:
  title: Journal of Geophysics
  short_title: J. Geophys
  url: https://journal.geophysicsjournal.com

or

yaml
venue:
  title: EuroSciPy 2022
  url: https://www.euroscipy.org/2022

(publication-metadata)=

Publication Metadata

MyST includes several fields to maintain bibliographic metadata for journal publications. First, it has first_page and last_page - these are page numbers for the article in its printed form. Also, volume and issue are available to describe the journal volume/issue containing the article. Each of these properties has the same fields available, described in @table-frontmatter-biblio.

{list-table} available volume and issue fields
:header-rows: 1
:label: table-frontmatter-biblio
* - Field
  - Description
* - `number`
  - a string or a number to identify journal volume/issue
* - `title`
  - title of the volume/issue, if provided separately from number
* - `subject`
  - description of the subject of the volume/issue
* - `doi`
  - the volume/issue DOI

An example of publication metadata for an article may be:

yaml
first_page: 1500
last_page: 1503
volume:
  number: 12
issue:
  name: Winter
  description: Special issue on software documentation
  doi: 10.62329/MYISSUE

These fields provide a more complete superset of publication metadata than the "biblio" object defined by OpenAlex API:

Old-timey bibliographic info for this work. This is mostly useful only in citation/reference contexts. These are all strings because sometimes you'll get fun values like "Spring" and "Inside cover."

If MyST frontmatter includes an OpenAlex biblio object, it will be coerced to valid publication metadata.


Glossaries And Terms (`docs/glossaries-and-terms.md`)


title: Glossaries, Terms, Index Pages, and Abbreviations short_title: Glossaries, Terms, & Index Pages

You can define Terms and generate reference pages for them with Glossaries and Index Pages. This allows you to centralize definitions and pointers to where various items are mentioned throughout your documents.

:::{seealso} See our index and glossary page

:::

Glossaries

Glossaries are a collection of definitions for Terms in your documents.
Below is the example of a glossary defining the terms {term}glossary, {term}term, and {term}Index

:::{glossary}
glossary
: A glossary is a list of terms and their definitions.

term
: A term is a word with a specialized meaning.

index
: An organized list of information in a publication.

index entry
: A word or phrase that has been marked for inclusion in the index with the index directive or role.
:::

To add a glossary to your content, add the {myst:directive}glossary directive with the content as definition lists.

{myst}
:::{glossary}
MyST
: An amazing markup language that supports glossaries
:::

You can use {term}`MyST` to create glossaries.

You can define multiple glossaries in your documents, as long as they do not re-define the same term.

:::{warning} Compatibility with {term}Sphinx and {term}reStructuredText
:class: dropdown
The glossary is very similar to the reStructuredText glossary, but uses definition lists instead of indentation to indicate the terms[^drawback]. For working with glossaries in Sphinx, you can use the following syntax:

markdown
```{glossary}
Term one
  An indented explanation of term 1

A second term
  An indented explanation of term 2
```

[^drawback]: Note that this has a challenge of not being able to have two terms for the same definition.

:::

Terms

To reference a term in a glossary use the {myst:role}term role:

  • {term}`MyST` produces {term}MyST
  • {term}`MyST Markdown <MyST>` produces {term}MyST Markdown <MyST>

The label that you use for the term should be in the same case/spacing as it appears in the glossary. If there is additional syntax (e.g. a link) in the term, the text only representation will be used. The term is rendered as a cross-reference to the glossary and will provide a hover-reference.

Index pages

Index pages show the location of various terms and phrases you define throughout your documentation.
They will show an alphabetized pointer to all {term}Terms <term> and {term}Index entries that you define.

Index entry directive

:::{warning}

The existing syntax for index directives and index roles has been taken directly from Sphinx. An improved MyST-specific syntax will likely be added in the future, and this Sphinx syntax may be removed.
:::

You can define index entries with Directives like so:

text
:::{index} my first index item
:::

% This won't show up in the content
:::{index} my first index item
:::

In this case, no text will be displayed with the directive, but an index entry will be created in your index that points to the location of the directive.

You can define a nested index entry with a semicolon (;)

text
:::{index} index parent; index child
:::

% This won't show up in the content
:::{index} index parent; index child
:::

Also, you can define multiple, non-nested index entries in the directive argument by delimiting with a comma:

text
:::{index} index one, index two, index three
:::

Pairs and Triples

You can generate two reciprocal, nested index entries with pair:, like so:

text
:::{index}
pair: index one; index two
:::

or for three related items:

text
:::{index}
triple: index one; index two; index three
:::

You can define multiple index entries in a single directive by putting them on multiple lines. In this case each entry must have a prefix of (single, pair, or triple to distinguish what specific nested entries are created).

text
:::{index}
single: index one
single: index parent; index child
pair: index two; index three
pair: index four; index five
triple: index six; index seven; index eight
:::

See and See Also

For one index entry to reference another index entry, you may use the see or seealso prefix. For example,

text
:::{index}
see: index 1; index one
:::

This creates an entry like "index 1: See index one"

The prefix seealso: behaves identically but uses the text "See also" instead of "See."

Emphasis

To emphasize any index entry, add an exclamation point before the index term. This will style the entry as either bold or italic and move it before other entries that refer to the same term but are not emphasized.

text
:::{index} ! index one
:::

Index entry role

You can define an index entry with a role like so: {index}`my second index`.
This includes the text "{index}my second index" in your content and creates an entry.

If you want the text to be different than the index entry, you may use the syntax {index}`text on the page `.

Index roles can use all the same prefixes as described for the {index} directive, including single, pair, triple, see, and seealso. For example, {index}`pair: index one; index two` or {index}`text on the page `.

Generate an index

You can show an index on a MyST page with the {show-index} directive.
For example:

text
```{show-index}
```

See the index for this documentation for what this generates.

(abbreviations)=

Abbreviations

To create an abbreviation, you can explicitly do this in your document with an abbreviation role, for example, {abbr}`HR (Heart Rate)`. You can also use the page or project frontmatter:

{myst}
---
abbreviations:
  RHR: Resting Heart Rate
  HR: Human Resources
---

To lower your RHR, try meditating or contact your local HR representative?

The abbreviations are case-sensitive and will replace all instances[^1] in your document with a hover-tooltip and accessibility improvements. Abbreviations in cross-references, code, and links are not replaced. For example, in this project we have a lot of abbreviations defined in our myst.yml:

[^1]: Abbreviations must be at least two characters!

Our OA journal ensures your VoR is JATS XML with a PID (usually a DOI) to ensure LTS.

  • TLA Soup

Order of Abbreviations

Abbreviations defined in your frontmatter are applied in longest-sorted order. If you have two abbreviations with the same suffix (e.g. RHR and HR), the longer abbreviation will always take precedence.
To have longer abbreviations not be transformed, or if that string is included in another word, explicitly set them to null in your frontmatter. In the following example, HTML will not have the letters ML as an abbreviation.

{myst}
---
abbreviations:
  ML: Machine Learning
  HTML: null
---

We use ML to parse HTML.

Glossary (`docs/glossary.md`)


title: Glossary & Index

(glossary-page)=

Glossary

:::{glossary}

CommonMark
: A standard syntax of Markdown that is used across many communities and projects.
It is the base flavour of Markdown for Jupyter Notebook, and the base flavour
for {term}MyST Markdown <MyST> and {term}Jupyter Book.

Project Jupyter
: The project and team that supports {term}Jupyter Book and {term}MyST Markdown <MyST> and the tools and packages behind them.

Executable Books Project
: The project and team that used to support {term}Jupyter Book and {term}MyST Markdown <MyST>. The project is currently stewarded by {term}Project Jupyter.

MyST
: MyST can either refer to {term}MyST Markdown or {term}MyST-CLI.

MyST Markdown
: A flavour of Markdown that was designed for scientific and technical writing.
: Sometimes referred to simply as {term}MyST.

MyST-CLI
: The MyST Markdown CLI can be used to create a website, PDF documents, and generally structure and parse a MyST project. You can install across various package managers using mystmd.
: Sometimes referred to simply as {term}MyST.

Sphinx
: A documentation engine written in Python. Sphinx supports many features that are
necessary for scientific and scholarly publishing. It is currently used by {term}Jupyter Book.

reStructuredText
: reStructuredText (RST) is a plain text markup syntax and parser system, the RST parser is a component of Docutils.

Binder
: A free, public service for running reproducible interactive computing environments.
Binder is a 100% open source infrastructure that is run by members of the Jupyter
community. The underlying technology behind the Binder project is {term}BinderHub.

BinderHub
: The underlying technology of https://mybinder.org, BinderHub is an open source tool that
runs on Kubernetes and utilizes a {term}JupyterHub in order to provide live, reproducible
interactive computing environments that users host on GitHub.

Google Colab
: A Jupyter Notebook service from Google that provides access to free computing resources,
including GPUs and TPUs.

JupyterHub
: A core open source tool from the Jupyter community, JupyterHub allows you to
deploy an application that provides remote data science environments to multiple
users. It can be deployed in the cloud, or on your own hardware.

Jupyter Book
: Jupyter Book is a distribution of {term}Sphinx that allows you to write content
in markdown and Jupyter Notebooks, execute content and insert it into your book,
and build a variety of outputs for interactivity and document publishing.

MyST Markdown
: MyST stands for "Markedly Structured Text". It is a flavor of Markdown that was designed to make it easy to write content that can be parsed by a {term}MyST Document Engine. MyST Markdown syntax was created to support functionality in the MyST specification like extensions, roles and directives, and cross-references. It was originally created for Jupyter Book via the MyST Parser for Sphinx, but now serves as a standard markdown syntax across the MyST ecosystem.

MyST Document Engine
: A MyST Document Engine builds {term}MyST AST according to the {term}MyST Specification[^myst]. For example, a MyST Document Engine might know how to transform a text file written in {term}MyST Markdown into JSON output that follows the {term}MyST Specification. The official MyST Document Engine is documented at mystmd.org/guide, and is maintained by the Jupyter Book team

[^myst]: Often we focus upon a MyST Engine's ability to parse {term}MyST Markdown. However, a MyST Engine may consume any kind of input markup as long as the result is a {term}MyST AST that follows the {term}MyST Specification. This is why the official MyST Document Engine can parse other kinds of markup, such as a subset of LaTeX.

MyST Specification
: A specification that describes the structure and function of the MysT AST. It provides a framework for defining all of the kinds of content and metadata that a MyST document can contain. For example, the MyST specification defines how sections can contain paragraphs, how paragraphs can contain sentences, and how sentences can contain a "bolded" chunk of text. The specification is defined at mystmd.org/spec.

MyST AST
: A structured representation of a MyST Document, typically built from a markup language (like MyST Markdown) by a {term}MyST Document Engine, that adheres to the {term}MyST Specification. Usually, a MyST Abstract Syntax Tree (AST) is represented as a JSON data structure, with metadata attached to each piece of content that describes its role, relationships with other content, etc. The AST allows us to separate the steps of parsing content (such as {term}MyST Markdown) from generating output like HTML, PDF, docx, etc. For an introduction to its structure, see the AST primer.

AST node
: A single node in the {term}MyST AST. It has an explicit type and contains 0 or more children.

Root AST node
: The top-level {term}node <AST node> in the {term}MyST AST. Its type is "root".
A MyST AST can contain only one node with a type of "root".

MyST Renderer
: An application that consumes {term}MyST AST and uses it to present the contents in a particular format. For example, the MyST Document Engine has a few built-in renderers. See the HTML renderer for an example that converts {term}MyST AST to HTML.

MyST Theme
: A template for converting {term}MyST AST into structured output using a {term}MyST Renderer. For example, a theme provides the structure and rules by which you could convert {term}MyST AST to different kinds of LaTeX output based on a number of different journal templates. It could also be a way of converting {term}MyST AST into different kinds of HTML for various kinds of websites. The MyST Templates GitHub Organization is a community-maintained resource for many kinds of themes / templates.
:::

(index-page)=

Index

{show-index}

Guiding Principles (`docs/guiding-principles.md`)


title: Guiding principles of MyST

This document describes some high-level principles that the MyST community follows in designing technology and deciding where we are going.
It is meant to align our team on the big ideas guiding the project, and provide visibility to others for the impact we're trying to have and where we are heading.

:::{note} This will evolve!
Our guiding principles are a constant work-in-progress, and will evolve as the community better-understands its opportunities and goals.
:::

Who is MyST for and what problem does MyST try to solve?

MyST builds composable tools and standards for people that communicate technical and computational narratives. These people need to communicate throughout the lifecycle of discovery and learning, both within their local networks and to broader communities. Below are several key problems that MyST aims to solve for this group:

Communicating with data: These problems apply to data scientists in general.

  • There is no document standard and engine that is well-defined, broadly adopted, and integrated with modern data science workflows. This results in a variety of formats for communication, which introduces friction to learning and re-using other people’s ideas and data.
  • Tools for interactive computing (like Jupyter Notebooks or JupyterLab) focus primarily on data exploration and discovery. Tools specifically designed for authoring and reading tend to be created by different communities, and thus do not integrate with data science workflows.
  • Tools for communicating tend to have strong assumptions about the output format (e.g. HTML or PDF) or about the use-case (e.g., publishing in peer-reviewed journals vs. collectively-written documentation), which leads to workflow-specific tools that are not interoperable.

Scholarly publishing: Because of the scholarly community’s unique relationship with journals, we define several problems unique to this area that we design for:

  • The publishing ecosystem is fractured, with a variety of formats and technology standards primarily focused on print-based mediums (e.g. PDFs). Authors lose time reformatting their content according to publisher needs, and static mediums do not well represent computational data.
  • Tools for publishing and communication are often distinct from the tools used for exploration and analysis. This makes it harder for computation to inform the scholarship, and creates artificial barriers between the analyses that define and reproduce ideas, and the scholarship that describes ideas.
  • Publishing systems are strongly linked to large-scale publishers, and do not optimize for accessible re-use of content. This makes it harder for researchers to learn and re-use one another's ideas.

How does MyST uniquely solve this problem?

What's the approach MyST takes that uniquely solves the problem(s) above? For example:

  • Provide a reusable Source of Truth. Define a "source of truth" document structure that is the primary output of MyST's build engine, and that can be flexibly rendered into many output formats.
  • Computation is a first class citizen. Define a document standard that treats computational elements as first-class citizens, and provides an excellent web-native interface, allowing authors to bring their ideas to life with interactive computation.
  • Continuous communication throughout the data science lifecycle. Build on pre-existing community standards for computational analysis, to ensure interoperability with pre-existing workflows and to allow users to communicate via these workflows rather than adopting significant new ones. We integrate with the Jupyter ecosystem because it is an industry standard in interactive computing for data exploration and discovery.
  • Extension points and customization get users over the last mile. Don’t design MyST for specific scientific domains or questions, and provide tools that are broadly useful and composable. Create tools and standards that facilitate interoperability and enable customization so that users can extend MyST’s functionality for their specific needs.
  • Community-led tools and standards. MyST should be led by its community in order to ensure it continues to represent the interests of its community members, and is not driven by a single stakeholder or organization.

Design principles to promote a rich and interconnected scientific commons.

  • Promote open science practices via good UX. As a general guiding principle, encourage users to adopt best practices by removing friction, offering an excellent user experience, and incentivizing them with useful functionality. Assume users will use what gives them the best experience and features, and bring open science workflows throughout.
  • Pull for reproducibility through interactivity. Build compelling ways for researchers to integrate computation into their narratives; this interactivity requires reproducible practices to be adopted and maintained.
  • Adding metadata and links creates immediate value. We design features that encourage and showcase additional information when authors provide valuable metadata (DOIs, ORCIDs, RORs) as well as cross-reference other resources. For example, by pulling in references dynamically and providing rich hover information and contextual information.
  • Data should be easier to add than markup. Given the choice of, for example, writing a text only reference in APA style or adding a single markdown link to a DOI, encourage authors to choose to work with data and linked metadata services. Our tools should encourage and reinforce using data as the primary source of information.
  • Bring contextual information to the reading experience. The user interfaces we design should promote bringing information, such as equations, figures, abbreviations, citations, or terms and visualizations from another project directly into the reading experience. This design principle reinforces the community, standards-based ecosystem that we are curating.

What does success look like for MyST?

We imagine a future where MyST is an industry-wide standard for communicating with computational narratives that provides a single toolchain for researchers to communicate their ideas wherever they wish.

How do I pronounce MyST?

Here's an overly helpful collection of ways to say MyST:

Like the word "missed".

Like the natural phenomenon caused by small droplets of water aerosols suspended in the cold air, usually by condensation.

Like /mɪst/.

Like the video game.


In Page Execution (`docs/in-page-execution.md`)


title: In-Page Execution
subtitle: Connecting Jupyter Kernels to your MyST website
thumbnail: thumbnails/integrating-jupyter.png
aliases:

  • integrating-jupyter

In-page execution allows you to connect a live Jupyter kernel to your website, allowing you to add interactive visualizations and interactivity that is powered with a live kernel. This allows your readers to reproduce the outputs of your document in real-time, and to add more complex interactive content via tools like ipywidgets or Panel.

Here are a few back-ends that you can use to provide the computation for in-page execution:

  • Binder (e.g., mybinder.org)
  • WASM-based in-browser kernels with JupyterLite
  • Local or remote Jupyter servers you spin up locally or via a service like JupyterHub
{attention} this is a beta feature
We are in active development and this feature should be considered `beta` - please help us out [report any issues that you find](https://github.com/jupyter-book/mystmd/issues).

Watch for the 🚧 icons on headings that are still work in progress.

Enable in-page execution

To enable in-page execution, use the project.jupyter: true parameter like so:

{code} yaml
:filename: myst.yml
version: 1
project:
  jupyter: true

By default, MyST will use mybinder.org to build and provide the environment for computation. It will use the thebe-binder-base repository to build the environment. If you have a GitHub repository defined for your project via project.github, then it will use that instead.

Launch in-page execution

To launch in-page execution on the page, press the "power" button at the top of the page content.

This will launch a Binder session (or, connect to a live Jupyter server based on your configuration). If you launch a Binder server, you'll see a box in the bottom-right of the page showing the status of Binder launching your session.

You'll know that execution is now available when the in-page execution panel is visible at the top of the page. It looks like this:

{figure} ./images/thebe-control-panel.png
This panel allows you to control the execution on the current page.

In addition, all cells on the page will execute when the server is available.

Launch into the interactive Jupyter server

To change the browser window to the fully interactive Jupyter server that is powering your computation, click the "Launch Jupyter Server Window" button to the right of the in-page execution panel.

{figure} ./images/thebe-control-panel.png
The right-most button will take you to the fully interactive Jupyter Server.

Binder configuration options

By default, in-page computation uses a BinderHub service to provide computation.

Options for Binder configuration

{list-table}
:header-rows: 1

* - key
  - required?
  - description
  - default
* - `repo`
  - required
  - The repository to use as a base image for your Jupyter server as a `owner/reponame` string or a fully qualified url. See [](#binder-option-repository).
  - `executablebooks/thebe-binder-base`
* - `ref`
  - optional
  - The git ref to use from the repository, allow you to target a specific branch, tag or commit. See [](#binder-option-branch).
  - `HEAD`
* - `url`
  - optional
  - The base url of the binder service to connect to. See [](#binder-option-service).
  - `mybinder.org`
* - `provider`
  - optional
  - Tells `thebe` how to form urls for requesting binder services. Can be one of `github`, `gitlab` or `git`. See [](#binder-option-repoprovider).
  - `github`
{attention} be aware of multiple keys
There are two possible locations for `binder` keys:

1. `project.binder` is for [launch button configuration](./website-launch-buttons.md), and will take the user to a new page with a Binder session.
2. `project.jupyter.binder` is for in-page computation described here.

(binder-option-repository)=

Choose the repository for your kernel environment

To choose the repository that is used for your kernel, use the project.jupyter.binder.repo key. Here's an example:

{code} yaml
:filename: myst.yml
project:
  jupyter:
    binder:
      repo: https://github.com/binder-examples/requirements # default repo

By default, MyST will use the HEAD of the repository, which usually corresponds to the main branch. To choose a different branch, see .

{tip} equivalent syntax
:class: dropdown
The following cases are all equivalent:

```yaml
project:
  jupyter: true
```

```yaml
project:
  jupyter: 'binder'
```

```yaml
project:
  jupyter:
    binder: true
```

```yaml
project:
  jupyter:
    binder:
      repo: executablebooks/thebe-binder-base # default repo
```

```yaml
project:
  jupyter:
    binder:
      repo: https://github.com/executablebooks/thebe-binder-base # default repo
```

(binder-option-branch)=

Choose a branch or ref for your environment

To choose a branch or git ref other than the default (HEAD), use the ref key like so:

{code} yaml
---
filename: myst.yml
caption: A complete `thebe.binder` configuration
---
project:
  jupyter:
    binder:
      # ref can be the name of a branch or a commit hash
      ref: my-branch

(binder-option-service)=

Choose the BinderHub service for computation

To choose a BinderHub service other than mybinder.org, use configuration like so:

{code} yaml
---
filename: myst.yml
caption: A complete `thebe.binder` configuration
---
project:
  jupyter:
    binder:
      # The URL parameter controls the BinderHub serviec to use.
      url: https://binder.myorganisation.com/services/binder/

(binder-option-repoprovider)=

Choose a repository provider other than GitHub

To choose a git repository provider other than GitHub, use the provider key like so:

{code} yaml
---
filename: myst.yml
caption: A complete `thebe.binder` configuration
---
project:
  jupyter:
    binder:
      # See below for a list of available providers
      provider: gitlab

Configure my Binder repository environment

See the Binder documentation for instructions on how to set up your repository to have the environment you need. Binder uses the Reproducible Execution Environment Specification to turn repository contents into a reproducible environment.

(thebe-jupyter-server)=

Use a local or remote Jupyter server for in-page execution

If you have access to a Jupyter server that is already running (for example, by starting a server locally, or accessing a server via a JupyterHub), you can use this to provide in-page computation instead of Binder.

To use a server that is already running locally, use the jupyter.server key like so:

{code-block} yaml
---
filename: myst.yml
caption: A unique connection token must be supplied to connect to a local server
---
project:
  jupyter:
    server:
      url: http://localhost:8888/
      token: <your-secret-token>
{list-table} the configuration options and their defaults for local kernel computation.
:header-rows: 1

* - key
  - description
  - default
* - `url`
  - The base url of the Jupyter server you want to connect to
  - `http://localhost:8888`
* - `token`
  - The secret token string required by your Jupyter server

(start-a-local-jupyter-server)=

Start a local Jupyter server for in-page computation

In addition to how you might normally start a JupyterLab session, it's necessary to provide two additional command line options, as follows.

{code} bash
jupyter lab --NotebookApp.token=<your-secret-token> --NotebookApp.allow_origin='http://localhost:3000'

The command above is fine for local development. The token used should align with that provided in the project.thebe.token key and allow_origin should allow connections from your myst site preview, usually running on http://localhost:3000.

When starting a local Jupyter server for use with MyST it's also important to understand your computational environment and ensure that the Jupyter instance has access to that with the dependencies it needs to run. This is achieved by following normal best practices for reproducible environment configuration, if you're not familiar with these see REES.

{danger} securing a jupyter server
:class: dropdown
If you intend to run a dedicated single user Jupyter server accessible over a network please carefully read and follow [the advice provided by the Jupyter server team here](https://jupyter-server.readthedocs.io/en/stable/operators/security.html).

MyST Websites will work best, be safer and be more robust when backed by Jupyter services such as BinderHub or JupyterHub.

(jupyterlite)=

Use JupyterLite and WebAssembly for in-page computation

The JupyterLite project allows you to ship a lightweight computational environment that runs fully in your browser. To use JupyterLite for your in-page computation, use the configuration below:

{code} yaml
project:
  jupyter:
    lite: true

This will load the server using the default environment described in the JupyterLite docs. For more configuration options with JupyterLite, see .

{important} todo
Add the specific list options for custom wheel paths, etc.

Enable or disable in-page computation on a single page

To configure in-page computation on a single page, use document frontmatter. For example:

To enable in-page computation on a single page, use configuration like so:

{code} markdown
:filename: mypage.ipynb
---
jupyter:
  binder:
    # If you had one page that needed an R kernel
    repo: https://github.com/binder-examples/r
---

My page content.

To disable in-page computation on a single page, use configuration like so:

{code} markdown
:filename: mypage.md
---
jupyter: false
---

My page content.

Reference

Complete options schema

{code-block} yaml
project:
  jupyter: undefined(false) | boolean | object | 'lite' | 'binder'
    lite: boolean
    binder: undefined(false) | boolean | object
      url: string (url)
      provider: string (git | gitlab | github | or custom)
      repo: string (org-name/repo-name | url | string)
      ref: string (undefined | string)
    server:  undefined | object
      url: string (url)
      token: string (any)
    kernelName: string (any)
    disableSessionSaving: boolean (default: false)
    mathjaxUrl: string (url)
    mathjaxConfig: string (any)

Additional options

The following keys can all be used under the project.jupyter configuration.

{list-table}
:header-rows: 1

* - `key`
  - description
  - default
* - `kernelName`
  - The name of the kernel to request when starting a session
  - `python`
* - `disableSessionSaving`
  - When `false` (default) any server settings received from `binder` will be cached in local storage. On page refresh or future page load the save session info will be used provided the session is still activate and the max age (86400s) has not been exceeded
  - `false`
* - `mathjaxUrl`
  - Allows the default mathjax bundle loaded by the Jupyter Latex Typesetter to be changed
  - Mathjax 2.7.5 from `cdnjs.cloudflare`[^mathjax]
* - `mathjaxConfig`
  - Allows the default mathjax configuration string to be changed
  - `TeX-AMS_CHTML-full,Safe`

Inline Options (`docs/inline-options.md`)


title: Inline Options
subtitle: Concise specification of CSS classes, IDs, and attributes

thumbnail: thumbnails/inline-options.png


:::{warning} Inline Options are in Beta
The support for inline attributes is in beta and may have some bugs or limitations.
Please give feedback on GitHub.
:::

MyST Markdown has support for inline attributes for both roles and directives, allowing concise specification of CSS classes, IDs, and attributes. This complements other methods for defining options, making markup more expressive and flexible.

markdown
:::{tip .dropdown open="true"} Title
Tip Content
:::

This can also be used for roles:

{span .text-red-500}`Red text`

{span .text-red-500}Red text

Syntax Overview

The inline attribute syntax follows this pattern:

text
{role #id .class key="value" key=value}`content`

```{directive #id .class key="value" key=value}
content
```

Name (e.g. tip or cite:p)
: The directive or role name must come first. There must only be a single "bare" token.

ID (#id)
: Defines the label/identifier of the node

Class (.class)
: Adds CSS class(es).

Quoted Attributes (key="value")
: Supports attributes containing spaces or special characters.

Unquoted Attributes (key=value or key=123)
: Allows simpler attribute values when there are no spaces.

For directives, these can be mixed with other ways to define options on directives, classes are combined in a single space-separated string; other repeated directive options will raise a duplicate option warning.


Install Node (`docs/install-node.md`)


title: Install NodeJS subject: Advanced Installation subtitle: Install the NodeJS runtime that powers MyST

The MyST Command Line Interface (CLI) is built on NodeJS (node), a JavaScript runtime that is widely used in many projects including well-known Python projects such as Jupyter Lab. node comes with its own package manager called npm.

There are a number of ways to install node and you can choose one that is suitable depending on your platform and preferences.

:::{important} Node Versions

MyST currently supports node v22+. Note that odd-numbered releases of node are not long-lived and you should prefer even-numbered releases when installing (see Node release schedule).
:::

Following any of the install methods below, verify your installation and ensure that node and npm are available on your system PATH by opening a new terminal window or command line prompt and typing:

shell
% node -v
vX.Y.Z

Manual Installation (all platforms)

You can download an appropriate installer package for your platform by visiting https://nodejs.org/. LTS refers to the current Long Term Support version of nodejs and is the best choice for use with MyST.

Download the installer package, and follow instructions to execute the installer for your platform. The installer will automatically add node and npm to your system PATH.

🛠️ Next, up Installing MyST

`node` via `conda` / `mamba` (all platforms)

nodejs is available as a package on conda-forge, although a limited number of versions are available on that channel. If you are a conda user, installation is straightforward but please note that MyST requires even-numbered node versions, and odd-numbered releases can be found on conda-forge.

🛠️ The following command can be used to lock down the version you are installing, adjust as necessary for the even-numbered version you are targeting:

shell
(my-conda-env)% conda install -c conda-forge 'nodejs=24.*'
(my-conda-env)% node -v
v24.YY.Z

💡 Alternatively, you can create a new conda environment directly as a node environment:

shell
% conda create -yn my-node-env 'nodejs>=20,<21'
% ....
#
# To activate this environment, use
#
#     $ conda activate my-node-env
#
# To deactivate an active environment, use
#
#     $ conda deactivate

🛠️ Next, up Installing MyST

`nodeenv` via pip (all platforms)

nodeenv is a python package allowing you to create and manage node installations on your system via virtual environments. If you want to work in virtual environments for node alongside your python virtualenv this is the way to go.

🛠️ Install nodeenv:

shell
% pip install nodeenv

🛠️ Query available node versions:

shell
% nodeenv --list
% ... XX.YY.Z ...

🛠️ Create a new environment based on a specific node version, and activate it:

shell
% nodeenv -n XX.YY.Z node_env
% . node_env/bin/activate
(node_env) % node -v
vXX.YY.Z

Read more about nodeenv in their docs.

🛠️ Next, up Installing MyST

Node Version Manager - Linux/MacOS (`nvm`)

nvm is a convenient way to manage multiple node installations on a POSIX compatible system.

🛠️ Install nvm using the install script (docs):

shell
% curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.8/install.sh | bash
% nvm -v
0.40.8

💡 Note: on MacOS you can also install nvm via brew

🛠️ Next, install an initial (default) version of node:

shell
% nvm install 24
Downloading and installing node v24.YY.Z...
...
Now using node v24.YY.Z (npm vXX.YY.Z
%

Read more about nvm in their docs.


Installing (`docs/installing.md`)


title: Install the MyST Markdown Command Line Interface subtitle: Install the MyST CLI using your favourite package manager short_title: Install MyST

To install the MyST CLI, choose your preferred package manager. If you do not know what a package manager is, it is recommended that you install MyST with mamba from conda-forge.

(installing-myst-tabs)=
:::::{tab-set}
(installing-conda-forge)=
::::{tab-item} conda-forge

You can install using mamba (https://mamba.readthedocs.io), see Getting started with Miniforge.

🛠 Then install mystmd from conda-forge:

shell
mamba install -c conda-forge mystmd

You may also use conda which comes packaged with the Anaconda python distribution.

🛠 Then install mystmd from conda-forge:

shell
conda install -c conda-forge mystmd

::::
::::{tab-item} PyPI

🛠 Install mystmd:

shell
pip install mystmd

🛠 Ensure mystmd is ready for use:

MyST needs node (https://nodejs.org) in order to run correctly. If node is not already installed, starting myst will prompt you to install it:

shell
$ myst -v
Node.js (node) is required to run MyST, but could not be found.
Install Node.js in '/root/.local/share/myst/XX.YY.Z'? (y/N): y
Attempting to install Node.js in /root/.local/share/myst/XX.YY.Z ...
Successfully installed Node.js XX.YY.Z
...

:::{note} Installing Node.js Manually
:class: dropdown

The mystmd package on PyPI ships with the ability to install node (https://nodejs.org). If you would prefer to install NodeJS manually, see Installing NodeJS:
:::

::::
::::{tab-item} NPM

Ensure your node (https://nodejs.org) is up to date, see Installing NodeJS.

🛠 Then install mystmd using npm, yarn or pnpm:

shell
npm install mystmd

::::
:::::

This will install myst globally (-g) on your system and add a link to the main CLI tool. To see if things worked, try checking the version with:

shell
myst --version

This command should print the current version of the package. If all is good, you can type myst again in your terminal and it will list the help with all of the options available to you.

{note}
If you have any challenges installing, please [open an issue here](https://github.com/jupyter-book/mystmd/issues).

Dependencies for $\LaTeX$ and PDF

If you are exporting to $\LaTeX$ with an open-source template specified (see all templates) or if you are creating a PDF you will need to install a version of LaTeX.

:::::{tab-set}
::::{tab-item} conda-forge

🛠 Install mamba (https://mamba.readthedocs.io), see xref:biapol#ref:miniforge_python.

shell
$ mamba --version
mamba 1.5.8
conda 24.7.1

🛠 Install texlive-core, latexmk, and imagemagick from conda-forge:

shell
$ mamba install -c conda-forge texlive-core latexmk imagemagick

imagemagick is optional, but recommended if your documents include images that need conversion for PDF output.

For Debian or Ubuntu based systems, the equivalent packages commonly include texlive-xetex, texlive-fonts-recommended, texlive-plain-generic, latexmk, and imagemagick.

::::
:::::


Javascript Plugins (`docs/javascript-plugins.md`)


title: JavaScript Plugins

JavaScript plugins are native MyST plugins, which are loaded as modules into the MyST engine. Transforms defined in these modules have access to helpful AST manipulation routines made available by MyST. Edits to JavaScript plugins have no effect during execution of a MyST build, instead the build must be restarted.

(defining-a-new-directive)=

Defining a new directive

To create a plugin, you will need a single JavaScript file[^esm] that exports one or more of the objects above. For example, a simple directive that pulls a random image from Unsplash can be created with a single file that exports an picsum directive.

[^esm]: The format of the JavaScript should be an ECMAScript modules, not CommonJS. This means it uses import statements rather than require() and is the most modern style of JavaScript.

:::{literalinclude} picsum.mjs
:label: picsum-js-source
:caption: A plugin to add an picsum directive that includes a beautiful, random picture based on a size and optional ID string.
:::

This code should be referenced from your myst.yml under the projects.plugins:

{code} yaml
:filename: myst.yml
project:
  plugins:
    - picsum.mjs

Then start or build your document using myst start or myst build, and you will see that the plugin is loaded.

text
myst start
...
🔌 Lorem Picsum Images (picsum.mjs) loaded: 1 directive
...

You can now use the directive, for example:

markdown
:::{picsum}
:size: 600x250
:::

:::{picsum}
:size: 600x250
:::

If you change the source code you will have to stop and re-start the server to see the results.

Every field a directive or role can define is listed in .

Implementing a custom transform

Directives can be used to extend MyST with rich structured content. However, sometimes we want to modify the existing behavior of MyST. One of the ways to do this is by writing a custom transform. In this section, we'll implement a transform that replaces bold text with emphasis.

First, let's define the transform
:::{literalinclude} markup.mjs
:caption: A plugin to add a transform that replaces strong nodes with emphasis nodes.
:::
this code should be referenced from your myst.yml under the projects.plugins:

{code} yaml
:filename: myst.yml
project:
  plugins:
    - type: javascript
      path: markup.mjs

then start or build your document using myst start or myst build, and you will see that the plugin is loaded.
The stage field and the arguments a transform receives are described in .

text
myst start
...
🔌 Strong to emphasis (markup.mjs) loaded: 1 directive
...

you can now use the directive, for example:

markdown
I am **special bold text**, whilst I am **normal bold text**

I am special bold text, whilst I am normal bold text

(plugins:multi-page)=
:::{seealso} Aggregate information across multiple pages
A transform runs on one page at a time, but a plugin can still collect information from every page in a project and render it in one place - for example, for a site-wide glossary or a list of figures.
See the multi-page plugin demo for a working example and a walk-through of the pattern.
:::

Examples of plugins

The documentation you're reading now defines several of its own plugins to extend MyST functionality.
These are all registered in the documentation's myst.yml configuration with syntax like below:

{literalinclude} myst.yml
:start-at: plugins
:end-before: error_rules

Each plugin is defined as a .mjs file in the same folder as the documentation's MyST content.
Below is the contents of each file for reference.

::::{dropdown} Plugin: Latex rendering

{literalinclude} latex.mjs

::::

::::{dropdown} Plugin: Display an image

{literalinclude} picsum.mjs

::::

::::{dropdown} Plugin: Custom directive for documenting roles and directives

{literalinclude} directives.mjs

::::

::::{dropdown} Plugin: Render web template options as a table

{literalinclude} templates.mjs

::::


Math (`docs/math.md`)


title: Math and Equations short_title: Math & Equations thumbnail: ./thumbnails/math.png

There are several ways to make writing math in your documents as familiar as possible.
Math can either be (1) inline or (2) displayed as an equation block.[^katex]
In addition to the usual MyST syntax, you can also use "dollar math", which is derived from $\LaTeX$
and surrounds inline math with single dollar signs ($), and equation blocks with two dollar signs ($$).
The details of using inline math and equations are below.

[^katex]: The default MyST themes use KaTeX to render math in websites. This is similar to MathJax, which was used by Jupyter Book 1, but has a few differences. Almost all math should work the exact same way, but if you were using more custom LaTeX or MathJax workflows, you may need to investigate how to port them to KaTeX. See the KaTeX documentation.

For example, here is an example of a cross-product, which we reference in the docs below!

$$
\mathbf{u} \times \mathbf{v}=\left|\begin{array}{ll}u_{2} & u_{3} \ v_{2} & v_{3}\end{array}\right| \mathbf{i}+\left|\begin{array}{ll}u_{3} & u_{1} \ v_{3} & v_{1}\end{array}\right| \mathbf{j}+\left|\begin{array}{ll}u_{1} & u_{2} \ v_{1} & v_{2}\end{array}\right| \mathbf{k}
$$ (cross)

(inline-math)=

Inline Math

There are two ways to write inline math; (1) with a math role or (2) by wrapping it in single dollar signs.

{myst}
This math is a role, {math}`e=mc^2`, while this math is wrapped in dollar signs, $Ax=b$.

The output of these is the same (which you can see by looking at the AST and $\LaTeX$ outputs in the demo above).
Using a math role is much less likely to collide with your writing if it includes dollars (e.g. $2.99).

Occasionally, dollar signs that you do not intend to wrap math need to be escaped.
These can be preceded by a backslash, that is \$2.99, and the \ will not be displayed in your output.
If using $\LaTeX$ as an output, these dollar-signs will also be properly escaped again!

Equations

There are three ways to create an equation block:
(1) a math directive (rather than a role for inline math);
(2) wrap the equation in two dollar-signs, $$; or
(3) use a \begin{equation} statement directly (i.e. using AMS math).

Math directives

The math directive takes no arguments and the body of the directive is the $\LaTeX$ style math.
You can have an optional label parameter, which will label this equation for later cross-referencing, see below for more on that!

{myst}
```{math}
:label: my-equation
w_{t+1} = (1 + r_{t+1}) s(w_t) + y_{t+1}
```

See [](#my-equation) for more information!

(dollar-math)=

Dollar math equations

You can also create equations by wrapping content with two dollar signs ($$).
In this syntax, you can use the \label{label-name} command to add a label.
This can be quite convenient if your equations are small, the entire syntax can fit on a single line.

{myst}
$$
\label{maxwell}
\begin{aligned}
\nabla \times \vec{e}+\frac{\partial \vec{b}}{\partial t}&=0 \\
\nabla \times \vec{h}-\vec{j}&=\vec{s}\_{e}
\end{aligned}
$$

$$ \label{one-liner} Ax=b $$

See [](#maxwell) for enlightenment and [](#one-liner) to do things on one line!
{tip}
:class: dropdown
# Using AMS Math Environments

In addition to a `math` directive and dollar-math syntax, you can also use AMS math, which is specifically using [AMS Version 2.1](http://anorien.csc.warwick.ac.uk/mirrors/CTAN/macros/latex/required/amsmath/amsldoc.pdf).

```{myst}
\begin{equation}
\label{matrix}
Ax = b
\end{equation}

The general matrix equation is shown in [Equation %s](#matrix).
```

You can label your equation with the standard `\label{my-equation}` that you would do in $\LaTeX$.
The equation cross-referencing and numbering will work with the rest of your content.

```{warning}
The label implementation does not yet work for `sub-equations` and may not work if you have more than one label.
```

(ams-environments)=

## Supported AMS Environments

equation
: basic equation environment, similar to a math directive or dollar-math

multline
: variation equation, used for equations that don’t fit on a single line

gather
: a group of consecutive equations when there is no alignment desired among them

align
: used for two or more equations when vertical alignment is desired
: Note that `aligned` (i.e. with the `ed`) is **not** an AMS environment and only works inside of `\begin{equation}` or a math environment.

alignat
: allows the horizontal space between equations to be explicitly specified.

flalign
: stretches the space between the equation columns to the maximum possible width

matrix, pmatrix, bmatrix, Bmatrix, vmatrix, Vmatrix
: The pmatrix, bmatrix, Bmatrix, vmatrix and Vmatrix have (respectively)
`()`,`[]`,`{}`,`||`,and `‖‖` delimiters built in.

eqnarray
: eqnarray is another supported math environment, it is not part of amsmath, and it is better to use
align or equation+split instead

(referencing-equations)=

Referencing Equations

As you have seen above, any equation can be labelled and cross-referenced in other parts of your document.
For example, the start of this document had Equation %s, which can be referenced here with a link and inline-preview.
There are a few different ways to reference equations, with more details in .

Labelling equations

The examples above all show how to label an equation in the interactive demos.
With a directive, you can use the label option;
with dollar-math and AMS math, you can use the \label{} syntax that is native to $\LaTeX$.

For example, a directive can be labelled as follows:

md
```{math}
:label: my_label
my_math
```

To reference equations you can use a markdown link with the target, for example:
[](#cross) will create:

{note}
:class: dropdown
# Coming from Sphinx? How to use `eq` and `numref` roles.

We recommend using markdown links, as the syntax is consistent across all references and citations. You can also have markup inside of the text link and dynamically insert enumeration. However, if you are familiar with roles in Sphinx, you can also use the `eq` and `numref` roles to cross-reference equations.

The `eq` role can be used for referencing math, and by default inserts the number in parenthesis,
for example, {eq}`cross`. The label should be the first and only entry in the body of the role.
The `numref` directive should be used if you want to refer to your equation with text or a non-standard format.
As with other references you can use the `%s` or `{number}` to fill in the equation number (see [](./cross-references.md)).
Note that `{name}` does not work for equations as there is no text content to fill in.

````{myst}
```{math}
:label: my_label
w_{t+1} = (1 + r_{t+1}) s(w_t) + y_{t+1}
```

- Reference using `eq`: {eq}`my_label`
- Reference using `numref`: {numref}`my_label`
- Reference using `%s` `numref`: {numref}`Eq. %s <my_label>`
- Reference using `{number}` in `numref`: {numref}`Equation {number} <my_label>`
````

```{warning}
If you are using Jupyter Book or Sphinx, there are the following limitations:
(1) the `label` analysis of the source is not yet implemented;
(2) you can not reference equations using the {myst:role}`numref` role; and
(3) labels must not have spaces or start with a number (this is good practice anyways!).
```

Customizing Numbering

To change the reference format for math, you can use the frontmatter under the numbering field see numbering.
To override numbering for a specific equation you can use the {myst:directive}math.enumerated option on the math directive.

markdown
:::{math}
:enumerated: false
Ax = b
:::

(math-macros)=

Math Macros

Macros allow you to create reusable math components that can simplify the writing of a document.
These macros can be defined for a single document through the frontmatter, or shared in project frontmatter.
These macros are used throughout HTML and $\LaTeX$ exports and are written declaratively so that they can be easily parsed. Macros are the same as \newcommand or \renewcommand in $\LaTeX$, and use the math object in the frontmatter.

% Note: there must be a space here, or it is interpreted as {myst} options.

{myst}
---
# Math frontmatter:
math:
  # Note the 'single quotes'
  '\dobs': '\mathbf{d}_\text{obs}'
  '\dpred': '\mathbf{d}_\text{pred}\left( #1 \right)'
  '\mref': '\mathbf{m}_\text{ref}'
---

The residual is the predicted data for the model, $\dpred{m}$, minus the observed data, $\dobs$. You can also calculate the predicted data for the reference model $\dpred{\mref}$.

The math macros are parsed as yaml in the frontmatter and can easily be shared or inherited through project to page frontmatter. These are also inserted into any $\LaTeX$ outputs for creating professional PDF documents.

{important}
When using the YAML syntax for the `math` macros, use **single quotes** around the strings. The single quote yaml syntax means you do not have to text-escape the strings, otherwise backslashes `\f`, `\n`, `\b`, `\r`, `\t` and other symbols have to be escaped which is difficult to remember and leads to all sorts of strange errors.

The key is the command that you are defining, in the demo above \dobs, \dpred, and \mref; the command should include the leading \. The value of the entry should be the macro definition. If the definition contains #1 then there will be one required argument for the macro that should be supplied in braces when you use it (e.g. \dpred{m}). The macros can be nested as in the example where \dpred{\mref} uses two macros.

In the macro in the example above, \mathbf{d}_\text{pred}\left( #1 \right), the #1 is the first and only required argument, and is placed in between left and right brackets. The numbering for arguments starts at one, and other arguments can be added with #2, #3, etc. and then input in a command using \command{arg1}{arg2}.

{seealso}
In the future the information collected in the math macro will expand to include alt text, color, or interaction information (e.g. hover, substitution) to improve accessibility and interactivity.

(typst-math)=

Typst-specific Math Content

When exporting to Typst format, you can provide Typst-specific math content using the typst option. This allows you to use native Typst syntax instead of relying on tex-to-typst conversion, which may give incorrect results.

Example with typst argument in a math block:

{myst}
```{math}
:typst: root(3, x)
\sqrt[3]{x}
```

When the typst option is provided, it will be used directly in Typst output instead of converting the LaTeX content.

For inline math, you can also use the typst option with the math role:

{myst}
{math typst="typst"}`latex`

:::{important} Improve the Math Conversion
If your LaTeX math does not convert automatically to typst, please open an issue here.
:::


Md Vs Ipynb (`docs/md-vs-ipynb.md`)

How to choose between `.ipynb` and `.md` when writing Jupyter notebooks

You can work with Jupyter notebooks in both the .ipynb format as well as in a text-based format like MyST Markdown. This is a brief guide to help you choose when to use one vs. the other.

This is a question of trade-offs, not right and wrong. There are benefits and drawbacks to using either the more-structured .ipynb format, or the more human-readable .md format. We'll try to describe each of them below.

Key differentiators between `.md` and `.ipynb`

.ipynb is a JSON-based format that captures the entire interactive session, including code, Markdown, outputs, and detailed metadata. It is designed for machine readability and preserving execution state. It often results in very complex diffs when doing code review, because there is a lot of machine-generated information inside an .ipynb file. However, this also means that it preserves more information about the result of your computations inside the file itself.

.md is a plain text file with Markdown inside. It is more human-readable, and slightly less-structured than JSON files. It focused on the input cells and Markdown cells of a Jupyter Notebook, as well as human-generated metadata (like cell tags). Markdown notebooks do not store outputs. If you execute a cell with a Markdown-based Jupyter Notebook, then outputs will exist in your interactive session but not be saved to the .md file. This makes code review and version control much easier because you're only looking at Markdown and some human-generated syntax for code cells. Finally, Jupyter UIs require installing either Jupytext or JupyterLab-MyST to read Markdown-based notebooks.

Is the experience of reading and writing any different between `.md` and `.ipynb`?

In general, there isn't any difference between reading and writing these two formats.
If you use Jupytext or JupyterLab-MyST, then your experience in a Jupyter interface will be the exact same. It is only when you save the notebook that it gets written to .md or .ipynb. Here is the process in either case:

One exception to this is loading a notebook from disk. If using a .md file, you may need to take an extra UI action to load the .md file as a notebook (instead of as a text file). In addition, if you're using a .md file then the outputs will be empty every time you load it (because they aren't stored with the file).

Finally, if you're using a .md file, then you'll be able to edit it from any text editor, rather than requiring a notebook UI to load. You can read and understand the structure of a .md file on your own without needing to render it in a browser.

When to lean towards Markdown (`.md`)

  • Version control clarity: When tracking changes to the notebook's content (code and narrative) in a collaborative setting is crucial. Markdown's readability makes diffing straightforward.
  • Human-focused authoring: If you prioritize writing and reviewing the source content in a highly readable, human-editable format.
  • Collaborative review: For workflows involving frequent reviews of the code and explanations, where output changes are less critical to the review process.
  • Integration with text-based workflows: When you intend to use notebooks with tools that excel with Markdown, such as static site generators or documentation pipelines.
  • When re-executing your content is important or easy: When you want to re-execute your content at build-time, Markdown-based formats are usually easier to work with. This allows you to ensure your code is reproducible, so long as it isn't extremely complex or costly to do execute. This is often the preferred case in tutorials for libraries where notebooks are regularly executed in CI to ensure continued functionality.

When to lean towards `.ipynb`

  • Output preservation: When you need to store and share the results of code execution (text, tables, figures) without requiring re-running.
  • Out-of-the-box Jupyter UI support: When you cannot assume that your reader can install jupytext or jupyterlab-myst.
  • Computational intensity: For notebooks with lengthy or resource-intensive computations, saving outputs via .ipynb avoids redundant runs.
  • Self-contained execution history: When the complete record of an interactive session, including execution order and timestamps, is important.
  • Rich interactive elements: If your notebook heavily utilizes complex widgets or interactive visualizations whose state needs direct preservation.

Reminder: there is not a single right answer

People have debated the ".ipynb vs. .md" question for many years. There is no single answer that works for every use-case. Hopefully the ideas above give a general idea for when to use one vs. the other.


Myst.Yml (`docs/myst.yml`)

version: 1
project:
title: MyST Markdown
github: https://github.com/jupyter-book/mystmd

Uncomment to test launch buttons

jupyter:
binder:
repo: https://github.com/jupyter-book/thebe-binder-base
license:
code: MIT
content: CC-BY-4.0
subject: MyST Markdown
references:
python: https://docs.python.org/3.13/
jupyterbook: https://jupyterbook.org/en/stable/
jtex: https://mystmd.org/jtex/
spec: https://mystmd.org/spec/
biapol: https://biapol.github.io/blog/

plugins:
- directives.mjs
- picsum.mjs
- latex.mjs
- error-rules.mjs
- versions.mjs
- templates.mjs
- type: executable
path: picsum.py
- type: executable
path: markup.py
- type: javascript
path: markup.mjs

error_rules:
- rule: link-resolves
severity: ignore
keys:
- /robots.txt
- /sitemap.xml
- /jtex/template-yml
- /jtex
- /jtex/create-a-latex-template
- /jtex/create-a-beamer-template
- /jtex/contribute-a-template

toc:
- file: index.md
- file: installing.md
- title: Tutorials
children:
- file: quickstart.md
- file: quickstart-myst-documents.md
- file: quickstart-executable-documents.md
- file: quickstart-static-exports.md
- file: quickstart-myst-markdown.md
- file: quickstart-jupyter-lab-myst.md
- title: Configuration and setup
children:
- file: configuration.md
- file: frontmatter.md
- file: settings.md
- file: table-of-contents.md
- file: document-parts.md
- file: authorship.md
- title: Authoring
children:
- file: typography.md
- file: admonitions.md
- file: figures.md
- file: math.md
- file: tables.md
- file: code.md
- file: cross-references.md
- file: external-references.md
- file: embed.md
- file: citations.md
- file: proofs-and-theorems.md
- file: exercises.md
- file: blocks.md
- file: diagrams.md
- file: asides.md
- file: dropdowns-cards-and-tabs.md
- file: glossaries-and-terms.md
- file: writing-in-latex.md
- file: inline-options.md
- title: Executable Content
children:
- file: execute-notebooks.md
- file: notebooks-with-markdown.md
- file: reuse-jupyter-outputs.md
- file: interactive-notebooks.ipynb
- file: in-page-execution.md
- file: website-launch-buttons.md
- file: notebook-configuration.md
- file: md-vs-ipynb.md
- title: Websites
children:
- file: website-templates.md
- file: website-navigation.md
- file: website-metadata.md
# - file: themes-and-extensions.md
- file: seo-and-social.md
- file: website-style.md
- file: accessibility-and-performance.md
# - file: search.md
- file: analytics.md
- file: website-downloads.md
- file: deployment.md
children:
- file: deployment-github-pages.md
- file: deployment-gitlab.md
- file: deployment-curvenote.md
- file: deployment-netlify.md
- file: deployment-readthedocs.md
- file: deployment-webserver.md
- file: website-landing-pages.md

text
# - file: publishing.md
- title: Document exports
  children:
    - file: documents-exports.md
    - file: creating-pdf-documents.md
    - file: creating-word-documents.md
    - file: creating-jats-xml.md
    - file: creating-citation-cff.md
- file: plugins.md
  children:
    - file: javascript-plugins.md
    - file: plugins-distribute.md
    - file: executable-plugins.md
    - file: widgets.md
    - file: plugins-debug.md
    - title: Use cases
      children:
        - file: plugins-ast.md
        - file: plugins-dry.md
    - title: Plugins Reference
      file: plugins-reference.md
- title: Reference
  children:
    - file: install-node.md
    - file: update-myst.md
    - file: commonmark.md
    - file: syntax-overview.md
    - file: directives.md
    - file: roles.md
    - file: glossary.md
    - file: versions.md
- title: About MyST
  children:
    - file: overview.md
    - file: guiding-principles.md
    - file: background.md
- title: Contribute
  children:
    - file: contributing.md
    - file: developer.md
    - file: theme-developer.md
    - file: contribute-templates.md
    - file: contribute-docs.md
    - file: contribute-build-locally.md
    - file: contribute-add-feature.md
    - file: developer-api.md
- file: cite.md

extends:

  • abbreviations.yml

site:
title: MyST Markdown Guide
#template: ../../myst-theme/.deploy/book/template.yml
domains:
- mystmd-guide.curve.space
options:
style: public/style.css
analytics_plausible: jupyterbook.org
parts:
# This just tests this locally, it shouldn't be in the mystmd.org/guide site
# Note the CSS won't work because we aren't pulling it in
footer: https://github.com/jupyter-book/jupyterbook.org/raw/refs/heads/main/docs/_site/footer.md


Notebook Configuration (`docs/notebook-configuration.md`)


title: Add metadata to notebooks

In addition to global controls that you can set in the project settings, you can also add metadata to individual notebooks, or on notebook cells, to control how MyST handles them during execution and rendering.

(notebook-tags)=

Tags for an entire notebook

The concrete syntax of notebook tags depends on the notebook format.

(notebook-tags-ipynb)=

ipynb notebooks

Using Jupyter Lab, you can use the Property Inspector builtin extension to manage notebook-level metadata.

:::{image} /images/jupyterlab-property-inspector.png
:::

Using this tool you can add key-value pairs to the notebook metadata. For
example, to skip execution of a notebook, you would add the following key-value
pair inside the notebook metadata dictionary:

json
"tags": [
        "skip-execution"
    ]

Markdown notebooks

With Markdown notebooks, you can add notebook-level metadata using the YAML frontmatter at the top of the file. For example, to skip execution of a notebook, you would add the following to the frontmatter:

markdown
---
kernelspec:
  name: python3
  display_name: Python 3

execute:
  skip: true
---

(notebook-cell-tags)=

Tags for a cell in a notebook

Tags are a list of strings under the tags key in the cell metadata, which can
be set in JupyterLab, VSCode or in a {myst:directive}code-cell directive. Here
again the actual syntax depends on the notebook format.

ipynb notebooks

In the JSON representation of a jupyter notebook, cell tags would look like:

json
{
  "cell_type": "code",
  "source": ["print('hello world')"],
  "metadata": {
    "tags": ["my-tag1", "my-tag2"]
  }
}

You can use the Property Inspector builtin extension in Jupyter Lab to manage cell metadata, like so:

:::{image} /images/jupyterlab-cell-tags.png
:::

Markdown notebooks

In addition, MyST also supports the {myst:directive}code-cell directive for markdown notebooks; here is an example of adding the raises-exception tag on such a code cell:

In Markdown of a jupyter notebook these look like:

markdown
```{code-cell} python
:tags: [remove-input]
print("This will show output with no input!")
```

for a single tag, or more generally, if you need to add multiple tags:

markdown
```{code-cell} python
:tags: [my-tag1, my-tag2]
print("This will show output with no input!")
```

:::{table} Notebook cell tags with special meanings
:label: tbl:notebook-cell-tags

Tag Description
remove-cell Remove the cell from the rendered output.
remove-input Remove the code cell input/source from the rendered output.
remove-output Remove the code cell output from the rendered output.
hide-cell Hides the cell from the rendered output.
hide-input Hides the code cell input/source from the rendered output.
hide-output Hides the code cell output from the rendered output.
remove-stderr Remove the code cell output stderr from the rendered output. See also project config
remove-stdout Remove the code cell output stdout from the rendered output. See also project config
skip-execution Skip this cell, when executing the notebook
raises-exception Expect the code cell to raise an Exception (and continue execution)

:::


Notebooks With Markdown (`docs/notebooks-with-markdown.md`)


title: Executable Markdown Files subtitle: Define Jupyter metadata to make Markdown files executable kernelspec: name: python3 display_name: Python 3

{code-cell} python
:tag: remove-cell

import matplotlib.pyplot as plt
import numpy as np

Execution information can be added to MyST Markdown files, which allows you to perform computation using MyST's notebook execution engine. First, you will need to define a kernel specification, after which you can introduce Markdown-based computation in several ways:

:::{tip} Install Jupyter Server
:class: dropdown
To execute your code you will need to .
:::

(kernel-specification)=

Kernel specification

Defining a kernel specification (kernelspec) informs the Jupyter server of the name of the kernel that should execute your code. When you call myst build --execute or myst start --execute, the MyST CLI starts a Jupyter kernel to execute your code and gather the execution results. Defining different kernelspecs in each notebook makes it possible to flexibly switch the package environment and programming language (e.g. to use R in one notebook, and Julia in another).

The kernelspec configuration should be defined in the page-level frontmatter of each executable Markdown file (see for more information), and supports the same content that is validated by nbformat's schema:

{list-table} a list of available kernelspec fields
:header-rows: 1
:label: table-kernelspec

* - field
  - description
* - `name`
  - name of the kernel, e.g. `python3`
* - `display_name`
  - human-readable name for the kernel, e.g. "Python 3.12"

The following content is a frontmatter that defines a document that uses the python kernel:

{code} yaml
:filename: markdown-notebook.md
---
kernelspec:
  name: python3
  display_name: 'Python 3'
---

After we declare the frontmatter, the contents of each {myst:directive}code-cell directive and {myst:role}eval role will be executed by the python kernel during the building process.

Use a different kernel

Furthermore, you can build MyST Markdown content with other programming languages like JavaScript, R, and Julia by installing the corresponding kernel. For example, to build a page that uses JavaScript in the {myst:directive}code-cell, we could:

  1. Install a JavaScript kernel, e.g. ijavascript.
  2. Retrieve the kernel name with jupyter kernelspec list.
    In the default installation, the kernel name is javascript.
  3. Set the kernelspec in your document's frontmatter:
    yaml
    kernelspec:
      name: javascript
      display_name: JavaScript
  4. Define a code cell that uses the new kernel:
    markdown
    ```{code-cell} javascript
    console.log("hello javascript kernel");
    ```

(code-cell)=

Code cells with the {myst:directive}`code-cell` directive

You can use the {myst:directive}code-cell directive to create block-level computational outputs in MyST Markdown.

{myst:directive}code-cell directives have the following form:

text
```{code-cell} LANGUAGE
:key: value

CODE TO BE EXECUTED
```
  • LANGUAGE defines the language to be used in executing the code.
  • :key: value pairs will be treated as cell-level metadata.
  • CODE TO BE EXECUTED will be executed by the LANGUAGE kernel at build time.

For example, the following directive inserts a code cell into the page, and will be executed if you specify --execute with your MyST build.

markdown
```{code-cell} python
hello = "hello"
there = "there"
phrase = f"{hello}, {there}!"
print(phrase)
```
{code-cell} python
hello = "hello"
there = "there"
phrase = f"{hello}, {there}!"
print(phrase)

Add tags to {myst:directive}`code-cell` directives

You can add tags to the {myst:directive}code-cell directive.
They will be parsed and used in the same way that cell tag metadata is used in .ipynb files.

For example, the following code defines a remove-input tag (See all notebook tag options):

markdown
```{code-cell} python
:tags: [remove-input]
print("This will show output with no input!")
```

and results in the following:

{code-cell} python
:tags: [remove-input]
print("This will show output with no input!")

This can be particularly helpful for showing the output of a calculation or plot, which is reproducible in the {download}source code <./notebooks-with-markdown.md>, but not shown to the user like this matplotlib plot:

{code-cell} python
:tags: [remove-input]
# Data for plotting
t = np.arange(0.0, 2.0, 0.01)
s = 1 + np.sin(2 * np.pi * t)

fig, ax = plt.subplots()
ax.plot(t, s)

ax.set(xlabel='time (s)', ylabel='voltage (mV)',
       title='Waves in Time')
ax.grid()

fig.savefig("test.png")
plt.show()

For multiple tags you have two ways to provide them:

  • If you specify argument options with :, tags will be parsed as a comma-separated list of strings.
    For example:

    markdown
    ```{code-cell} python
    :tags: [tag1, tag2,tag3]
    # Note that whitespace is removed from tags!
    print("howdy!")
    ```
  • If you specify argument options with YAML, tags should be given as a YAML list.
    For example:

    markdown
    ```{code-cell} python
    ---
    tags:
    - tag1
    - tag2
    ---
    print("howdy!")
    ```

For more about how to specify directive options, see .

:::{seealso} Controlling cell visibility with tags
See for more information.
:::

(code-cell:linenos)=

Add line numbers and highlighting to {myst:directive}`code-cell` directives

You can add line numbers and emphasize specific lines in code cells, just like in code blocks.

text
```{code-cell} python
:linenos:
:emphasize-lines: 2,3

hello = "hello"
there = "there"
phrase = f"{hello}, {there}!"
print(phrase)
```
{code-cell} python
:linenos:
:emphasize-lines: 2,3

hello = "hello"
there = "there"
phrase = f"{hello}, {there}!"
print(phrase)

You can also use {myst:directive}code.lineno-start to start numbering from a specific line, and {myst:directive}code.filename to display a filename.

(myst:inline-expressions)=

Inline expressions with the {myst:role}`eval` role

You can use the {myst:role}eval role to evaluate code that is surrounded by text.
This allows you to quickly insert its output in a way that flows with the text around it.

:::{note} You can use inline expressions in the Markdown cells of an .ipynb file as well.
:::

For example, the following MyST Markdown would re-use the variable defined above.

markdown
The phrase is: {eval}`phrase`.

This results in the following:

The phrase is: {eval}phrase.

You can also modify the expression at the time of computation, for example:

markdown
The phrase manually computed is: {eval}`f"{hello}, {there} everybody!"`

This results in the following:

The phrase manually computed is: {eval}f"{hello}, {there} everybody!"

:::{seealso} Also works in JupyterLab
See for how these eval statements also work in JupyterLab.

:::

Markdown cells with block breaks

In , the jupytext tool for integrating text-based notebooks with existing Jupyter tools like JupyterLab is discussed. By default, when reading a MyST Markdown document, jupytext creates a single Markdown cell between adjacent code cells. The block-break (+++) syntax described in can be used to separate blocks of Markdown into distinct Markdown cells.

(compatibility-jupytext)=

Compatibility with `jupytext`

jupytext is a Python package that converts between Jupyter Notebooks (ipynb files) and plain text documents (like MyST Markdown files). It provides both a commandline tool to perform these conversions, and an extension for JupyterLab to facilitate opening text-based notebooks with the Notebook viewer. MyST Markdown is understood by jupytext, which defines a md:myst format for reading from / writing to MyST Markdown.

The following command will convert a MyST Markdown file example.md to the .ipynb notebook:

shell
$ jupytext --from md:myst --to notebook example.md

(execute-config)=

Control of execution

The top-level execute page frontmatter block accepts several properties for controlling build-time execution of the given page:

skip
: Skip execution of this notebook, even though it has a kernelspec.

depends_on_env
: Define the environment variables upon which this notebook depends. Changes to these variables will invalidate the execution cache.

cache
: Disable the execution cache for this notebook.


Package.Json (`docs/package.json`)

{
"name": "mystmd-docs",
"private": true,
"version": "0.0.0",
"dependencies": {
"myst-common": "workspace:",
"myst-directives": "workspace:
",
"myst-ext-button": "workspace:",
"myst-ext-card": "workspace:
",
"myst-ext-exercise": "workspace:",
"myst-ext-grid": "workspace:
",
"myst-ext-proof": "workspace:",
"myst-ext-tabs": "workspace:
",
"myst-migrate": "workspace:",
"myst-parser": "workspace:
",
"myst-roles": "workspace:",
"tex-to-myst": "workspace:
",
"unist-builder": "^3.0.0",
"unist-util-select": "^4.0.3"
}
}


Plugins Ast (`docs/plugins-ast.md`)


title: Generate MyST AST with Plugins

A common use case with plugins involves generating {term}MyST AST and inserting it into the document.
This page covers a few ways that you can do so.

:::{tip} New to the MyST AST?
Start with the AST primer for an overview of how MyST documents are structured at a high- and low-level.
:::

(plugins:ctx)=

Parse MyST markdown to AST in a directive or role

With a function call

The parseMyst function (available in the ctx variable) provides an explicit method to parse MyST text into {term}MyST AST.
Here's an example of using the parseMyst function within a directive plugin to parse the body of the directive into MyST AST:

{code} javascript
:filename: myplugin.mjs
const myDirective = {
  name: "mydirective",
  doc: "My new directive!",
  // Here the body is passed as a string
  body: { type: String, doc: "To be parsed into MyST AST." },
  run(data, vfile, ctx) {
    // Here we parse the body into MyST AST
    const ast = ctx.parseMyst(data.body);
    return ast.children[0];
  },
};

ctx.parseMyst returns a {term}root node <Root AST node> which contains the parsed MyST as children.
The directive plugin example above returns ast.children[0] (the first child of the root node) to avoid creating a malformed document (there can only be one "root").

You can use the parseMyst function to construct MyST outputs from text generated with string interpolation, for example:

{code} javascript
:filename: justacard.mjs
const myDirective = {
  name: "justacard",
  doc: "Basically does what a card directive does!",
  arg: { type: String, doc: "To be parsed into MyST AST." },
  body: { type: String, doc: "The body of the directive." },
  run(data, vfile, ctx) {
    // Here we parse a string that defines a multi-line card directive
    const ast = ctx.parseMyst(`:::{card} ${data.arg}\n${data.body}\n:::`);
    return ast.children[0];
  },
};

:::{tip} If you need to use multi-line strings you must dedent them
:class: dropdown

The example above puts a multi-line string onto one line by manually coding the \n characters.
If you instead want to show a multi-line string in your code, you will need to remove the indentation manually, for example like the following:

{code} javascript
:filename: justacard.mjs
const myDirective = {
  name: "justacard",
  doc: "Basically does what a card directive does!",
  arg: { type: String, doc: "To be parsed into MyST AST." },
  body: { type: String, doc: "The body of the directive." },
  run(data, vfile, ctx) {
    // By removing the indentation white-space the string is correctly parsed
    const ast = ctx.parseMyst(`
:::{card} ${data.arg}
${data.body}
:::`);
    return ast.children[0];
  },
};

:::

With automatic parsing

You can define a plugin's argument or body as {type: 'myst'} instead of {type: String} to enable those parameters to be autuomatically parsed into {term}AST nodes <AST node>.
In this case, data.body will be a list of AST nodes instead of text:

{code} javascript
:filename: myplugin.mjs
:linenos:
:emphasize-lines: 5
const myDirective = {
  name: "mydirective",
  doc: "My new directive!",
  // By defining type: 'myst', the directive body will automatically be parsed into MyST AST
  body: {
    type: 'myst',
    doc: "Pre-parsed and immediately usable as MyST AST.",
  },
  run(data) {
    return data.body[0];
  },
};

Use the MyST Sandbox to identify AST structure

The MyST interactive sandbox is a great way to explore what MyST looks like when it is rendered, and what its underlying AST structure looks like. This is particularly useful if you're generating MyST AST from scratch. For example, as part of a plugin role or directive.

:::{important}
Plugins must return "POST" AST, so ensure that the "POST" option at the top-right of the sandbox interface is selected!
:::

{figure} videos/sandbox-demo.mp4
Here's how you can use [the MyST sandbox](https://mystmd.org/sandbox) to explore the structure of a `card` directive and preview its AST.

:::{note} Click here to see the full output of the MyST sandbox
:class: dropdown

The output of the sandbox AST generator can be seen in @code:ast. The highlighted outer `root