Override and extend renderers

Your module returns a render map. Keys are AST node types (or nested variant selectors). Site maps merge after theme defaults, so the same key replaces the built-in renderer for your site.

Override an existing node

Return a built-in type key — for example admonition — and every note, warning, and tip on the site uses your component. No new Curvenote directive is required.

export default function ASTRenderer({ React, ASTComponent }) {
  const { createElement: h, useState } = React;

  function FunkyAdmonition({ node }) {
    // Custom chrome; still render title/body via ASTComponent
    return h('div', { className: 'funky-admonition' }, h(ASTComponent, { ast: node.children }));
  }

  return {
    admonition: FunkyAdmonition,
  };
}

Authors keep writing normal MyST Markdown:

:::{note} Still a note
Same AST type — different React component.
:::

Overriding admonition restyles built-in directives like note and warning site-wide.

Add a new directive and renderer

For a node type that does not exist yet, combine:

  1. A Curvenote directive in your plugin that emits a custom AST type.

  2. A renderer map entry whose key matches that type.

That is the fancy-note pattern: fancy-note{ type: 'fancyNote', … }{ fancyNote: FancyNote }. Authors write the directive; you own the React.

Showcase: filterable table

The most interesting pattern is often not a new directive. Attach behavior to an existing AST node with a variant selector, wrap it with your UI, then hand the children back through ASTComponent.

The filterable-table example selects only table containers marked filterable:

return {
  container: {
    'container[class="filterable"][kind="table"]': FilterableTable,
  },
};

Authors write a normal list-table in MyST Markdown:

```{list-table} Lab inventory
:header-rows: 1
:class: filterable

* - Name
  - Team
* - Ada Lovelace
  - Analytics
```

The component:

  1. Renders a search field and a “N of M rows” label.

  2. Filters body rows in JavaScript (header rows stay).

  3. Shallow-clones the container, removes the filterable class, and calls ASTComponent so the built-in container / table renderers draw the markup.

Important

Strip the selector class (or other matching attributes) before calling ASTComponent. If you leave filterable on the node, the variant selector matches again and you get an infinite loop.

Filter chrome from the site plugin; table cells still rendered by built-in NodeRenderers.

This pattern — interactive shell, built-in renderers underneath — is a good default whenever you want richer behavior without reimplementing tables, math, or nested markup.

Register modules as described in Add a render plugin to your site, and pick a packaging format that fits your workflow.

Abbreviations
AST
Abstract Syntax Tree
MyST
Markedly Structured Text