react-djot renders Djot into React elements with a
react-markdown-style API.
The design goal is familiarity: if you know react-markdown, you should feel
at home with react-djot.
- React 18+ support
componentsoverride map for per-node rendering control- No
dangerouslySetInnerHTML - React Server Component friendly main path (no hooks)
- TypeScript-first API
npm install react-djotPeer dependencies:
reactreact-dom@djot/djot
import { Djot } from "react-djot";
export function Example() {
return <Djot>{"# Hello\n\nThis is *Djot*."}</Djot>;
}import { Djot, compileDjot } from "react-djot";
<Djot
children={"# Title"}
components={
{
/* overrides */
}
}
/>;
const ast = compileDjot("# Precompiled");
<Djot ast={ast} />;Props:
children?: string | null | undefined- Djot source text to parse and render.
ast?: DjotNode- Precompiled Djot AST to render directly (bypasses parsing).
components?: DjotComponents- Optional map of node-tag keys to React components.
children and ast are mutually exclusive. Use one or the other.
The override pattern mirrors react-markdown: provide a component per node
type key.
import { Djot } from "react-djot";
import type { DjotComponents } from "react-djot";
const components: DjotComponents = {
para: ({ node, children, ...props }) => (
<p className="lead" {...props}>
{children}
</p>
),
heading: ({ level, node, children, ...props }) => {
const Tag = `h${Math.min(Math.max(level, 1), 6)}` as const;
return (
<Tag {...props} data-level={level}>
{children}
</Tag>
);
},
link: ({ node, href, children, ...props }) => (
<a {...props} href={href} rel="noreferrer" target="_blank">
{children}
</a>
)
};
export function Example() {
return <Djot components={components}>{"## Custom\n\n[Go](https://example.com)"}</Djot>;
}docsectiondivtablecaptionrowcellparaheadingemphstrongmarkandhighlightedsuperscriptandsupesubscriptinsertdeletespanfootnote_referencefootnoteendnotesdouble_quotedsingle_quotedsmart_punctuationsymbinline_mathdisplay_mathcodeverbatimcode_blockraw_blockraw_inlineurlemaillinkimagebullet_listordered_listlist_itemdefinition_listdefinition_list_itemtermdefinitiontask_listtask_list_itemblockquoteandblock_quotethematic_breakstrnon_breaking_spacesoft_breakandsoftbreakhard_breakandhardbreak
- Parses Djot via
@djot/djotparse() - Walks the AST recursively and creates React elements directly
- Does not use
dangerouslySetInnerHTML
Djot raw blocks and raw inlines are supported:
- Block:
~~~=html ... ~~~ - Inline:
`...`{=html}
Only html format is rendered by default. Other formats are ignored unless you
provide a components.raw_block or components.raw_inline override.
Autolink nodes are supported:
<https://pandoc.org/lua-filters>-><a href="https://pandoc.org/lua-filters">...<me@example.com>-><a href="mailto:me@example.com">...
Reference-style links and images resolve against document references:
[foo][bar]
![logo][img]
[bar]: https://example.com
[img]: /logo.png
Inline and block attributes are propagated to default rendered elements.
Parser-generated autoAttributes are also applied, and explicit attributes
take precedence on conflicts.
For nodes with built-in classes (for example task_list, inline_math,
display_math), user classes are merged with default classes.
Djot symbols (:alias:) render literally by default. You can provide a
components.symb override to map aliases to emojis or any custom output.
A bullet list item starting with [ ] (unchecked) or [x]/[X] (checked)
is a task list item:
- [ ] unchecked item
- [x] checked item
By default, task list items render as <li> elements with a disabled
<input type="checkbox"> prepended to the content. Use the task_list and
task_list_item override keys to customise this rendering:
const components: DjotComponents = {
task_list_item: ({ checkbox, children }) => (
<li data-checked={checkbox === "checked"}>{children}</li>
)
};Tight task lists (no blank lines between items) render item text inline with
the checkbox. Loose task lists preserve paragraph wrappers (<p>...</p>)
inside each item.
Styling note: browsers apply default list markers to <ul>/<li>. To match
Djot playground output (checkboxes without bullet dots), reset task-list
styles in your app CSS:
.task-list {
list-style: none;
padding-left: 0;
}
.task-list li {
list-style: none;
}
.task-list input[type="checkbox"] {
margin-right: 0.45rem;
}Definition list items are supported and render to semantic <dl>/<dt>/<dd>
elements:
: orange
A citrus fruit.
An escaped space (\ ) is parsed as non_breaking_space and renders as a
non-breaking space character.
<Djot /> has no hooks in the main render path, so it can be used in Server
Components.
npm run build
npm run typecheck
npm run lint
npm testA React + Vite demo app lives in examples/react-djot-demo.
npm run build
cd examples/react-djot-demo
npm install
npm run devMIT