On this page

Authoring variables

Rendering variables is <Markdown extensions={[variables({ data })]}>; see Variable basics. Authoring them uses the package's other extension, which runs in the editor.

Editing variables#

Variable support in the editor comes from an extension (there are no extra editor props for it).

import { MarkdownEditor } from '@react-markdown-kit/editor'
import { variableChips } from '@react-markdown-kit/variables/editor'


<MarkdownEditor
  value={source}
  onChange={setSource}
  extensions={[variableChips({ variables: reportVariables, previewData: sampleCustomer })]}
/>

In rich mode every placeholder shows up as a chip with its label (from the variables metadata) and a sample-data preview. When you type a placeholder it turns into a chip as soon as you type the closing }}, except inside code. The toolbar also gets an insert-variable button, and a custom toolbar can dispatch INSERT_VARIABLE_COMMAND from its own picker, built from the same variables map.

OptionPurpose
variablesMetadata per path: label and group for chips and pickers
previewDataSample values shown on the chips. Never written back
previewLocaleLocale used for preview formatting only
previewTimeZoneTime zone used for preview formatting only
formattersExtra formatters offered as suggestions and used for preview

variableChips() is one extension split into two halves that share a name. The root entry exports the headless half (the variable node, its round-trip serializer, and a renderer handler that shows an unresolved placeholder as <span data-rmk-variable>); /editor wraps it with the chip, the typing trigger and the command. Use whichever one your surface needs. A preset that includes the editor version still renders and resolves correctly, and variables({ data }) resolves chips like any other placeholder.

Preview never rewrites the source#

The whole feature is built around this rule. A variable node stores the authored text plus path, formatter and argument, and serializes from only those. previewData is for display and never feeds into serialization.

Authored     Hello {{customer.name}}
Rich mode    Hello [ Customer name ]
Preview      Hello Acme
Saved        Hello {{customer.name}}

If you change the preview customer, the saved source stays byte-for-byte the same. If the preview could edit the source, one wrong click could leave you with a document that says Acme to every customer.

The same goes for a failed preview. A sample value of the wrong type just shows no preview, and the placeholder isn't touched.

Two customers, one saved document
Authored sourcenever changes
Hello {{customer.name}},

Your plan renews on {{renewsAt | date:"long"}} at {{price | currency:"USD"}} per seat.
Resolved for Acmechanges

Hello Acme Industrial,

Your plan renews on May 2, 2026 at $24.00 per seat.

The left pane is what an author edits and what gets stored in the database. The right pane is what each customer gets.

Placeholders survive source mode#

In source mode the editor shows the authored {{customer.name}} instead of a preview value. Placeholders live inside text nodes, and the extension leaves inline code, fenced code and raw HTML alone, so anything the editor draws as a variable is something the engine would actually resolve.

Building your own picker#

Compile the source with variableChips() and no data. Every placeholder turns into a variable node with path, formatter and argument, and the variables metadata you passed in provides labels and groups. Then insert whatever the user picks with INSERT_VARIABLE_COMMAND on the native editor.

Next#

The overview, from placeholder syntax to schemas: Markdown variables.

Last updated on