API Reference
DjotConverter
The main entry point for converting Djot to HTML.
use Djot\DjotConverter;
$converter = new DjotConverter();
$html = $converter->convert($djotString);Constructor
public function __construct(
bool $xhtml = false,
bool $warnings = false,
bool $strict = false,
bool|SafeMode|null $safeMode = null,
?Profile $profile = null,
bool $significantNewlines = false,
?SoftBreakMode $softBreakMode = null,
bool $roundTripMode = false,
?BlockParser $parser = null,
?RendererInterface $renderer = null,
)$xhtml: Whentrue, produces XHTML-compatible output (self-closing tags like<br />).$warnings: Whentrue, collects warnings during parsing (see Error Handling).$strict: Whentrue, throwsParseExceptionon parse errors (see Error Handling).$safeMode: Whentrueor aSafeModeinstance, enables XSS protection (see Safe Mode).$profile: AProfileinstance for feature restriction (see Profiles).$significantNewlines: Whentrue, enables markdown-like parsing where block elements can interrupt paragraphs (see Significant Newlines Mode).$softBreakMode: Override how soft breaks are rendered. Whennull, uses the renderer's default (newline for HTML).$roundTripMode: Whentrue, adds round-trip metadata for Djot→HTML→Djot workflows (HTML renderer only).$parser: Optional pre-configured parser. When provided, inline parser constructor flags such aswarnings,strict, andsignificantNewlinesare ignored.$renderer: Optional pre-configured renderer. When provided, inline renderer constructor flags such asxhtml,safeMode,softBreakMode, androundTripModeare ignored.
Factory Methods
withSignificantNewlines()
public static function withSignificantNewlines(
bool $xhtml = false,
bool $warnings = false,
bool $strict = false,
bool|SafeMode|null $safeMode = null,
?Profile $profile = null,
?SoftBreakMode $softBreakMode = null,
bool $roundTripMode = false,
): selfCreates a converter with significant newlines mode enabled. See Significant Newlines Mode.
Methods
convert()
public function convert(string $input): stringConverts Djot markup to HTML.
When a Profile with maxLength is configured, the source-length limit is enforced here because the converter still has access to the original input string.
convertFile()
public function convertFile(string $path): stringConverts a Djot file to HTML. Throws RuntimeException if the file cannot be read.
$html = $converter->convertFile('/path/to/document.djot');parse()
public function parse(string $input): DocumentParses Djot markup into an AST Document without rendering.
When a Profile with maxLength is configured, the source-length limit is also enforced here.
parseFile()
public function parseFile(string $path): DocumentParses a Djot file into an AST Document. Throws RuntimeException if the file cannot be read.
$document = $converter->parseFile('/path/to/document.djot');
// Manipulate the AST...
$html = $converter->render($document);render()
public function render(Document $document): stringRenders an AST Document to HTML.
render() does not enforce source-length limits from Profile::setMaxLength(). That limit only applies on source-based entry points such as convert(), convertFile(), parse(), and parseFile(), where the original input length is known.
Registered extensions are reset before each render, so repeated convert() calls on the same converter start with fresh per-document extension state.
transform()
public function transform(Document $document, TransformerInterface ...$transformers): DocumentApplies one or more AST transforms and returns the transformed Document.
Use this when you want an explicit, reusable derived tree instead of hidden AST mutation during render().
When you call transform() on a converter configured for a specific renderer, renderer-aware transforms can preserve renderer-specific metadata such as HTML round-trip source attributes.
use Djot\Transform\HeadingLevelShiftTransform;
$document = $converter->parse($input);
$shifted = $converter->transform($document, new HeadingLevelShiftTransform(1));
$html = $converter->render($shifted);getParser()
public function getParser(): BlockParserReturns the block parser for direct access (useful for custom pattern registration).
getRenderer()
public function getRenderer(): RendererInterfaceReturns the currently configured renderer.
This may be HtmlRenderer, MarkdownRenderer, PlainTextRenderer, AnsiRenderer, or a custom renderer passed to DjotConverter::create() or the constructor.
getHtmlRenderer()
public function getHtmlRenderer(): HtmlRendererReturns the HTML renderer for direct configuration.
Throws LogicException if the converter is not using HtmlRenderer.
on()
public function on(string $event, Closure $listener): selfRegister a listener for render events. See Event System below.
off()
public function off(?string $event = null): selfRemove listeners. Pass event name to remove specific listeners, or null to remove all.
getWarnings()
public function getWarnings(): arrayReturns an array of ParseWarning objects from the last parse operation. Only populated when warnings: true is set.
hasWarnings()
public function hasWarnings(): boolReturns true if there were any warnings during the last parse operation.
clearWarnings()
public function clearWarnings(): selfClears any collected warnings.
setSafeMode()
public function setSafeMode(bool|SafeMode|null $safeMode): selfEnable, disable, or configure safe mode after construction. Pass true for defaults, a SafeMode instance for custom configuration, or null/false to disable.
Safe Mode
Safe mode provides built-in XSS protection for user-generated content.
Basic Usage
use Djot\DjotConverter;
// Enable with sensible defaults
$converter = new DjotConverter(safeMode: true);
$html = $converter->convert($userInput);What Safe Mode Does
URL Sanitization: Blocks dangerous URL schemes in links and images
- Blocked by default:
javascript:,vbscript:,data:,file: - Safe URLs like
https:,mailto:, and relative paths are allowed
- Blocked by default:
Attribute Filtering: Strips event handler attributes
- Blocks attributes starting with
on(e.g.,onclick,onload,onerror) - Blocks specific dangerous attributes (
srcdoc,formaction) - Allows safe attributes like
class,id,data-*
- Blocks attributes starting with
Raw HTML Handling: Controls how raw HTML is processed
escape(default): HTML-encodes raw HTML so it displays as textstrip: Removes raw HTML entirelyallow: Passes raw HTML through (not recommended)
SafeMode Class
use Djot\SafeMode;
// Factory methods
$safeMode = SafeMode::defaults(); // Standard protection
$safeMode = SafeMode::strict(); // Strips raw HTML completelyConfiguration Methods
// URL scheme control
$safeMode->setDangerousSchemes(['javascript', 'vbscript', 'data']);
$safeMode->addDangerousScheme('ftp');
$safeMode->getDangerousSchemes();
// Whitelist approach (only these schemes allowed)
$safeMode->setAllowedSchemes(['https', 'mailto']);
$safeMode->getAllowedSchemes();
// Attribute filtering
$safeMode->setBlockedAttributePrefixes(['on']); // Blocks onclick, onload, etc.
$safeMode->setBlockedAttributes(['srcdoc', 'formaction']);
$safeMode->getBlockedAttributePrefixes();
$safeMode->getBlockedAttributes();
// Raw HTML handling
$safeMode->setRawHtmlMode(SafeMode::RAW_HTML_ESCAPE); // Default
$safeMode->setRawHtmlMode(SafeMode::RAW_HTML_STRIP); // Remove completely
$safeMode->setRawHtmlMode(SafeMode::RAW_HTML_ALLOW); // Pass through
$safeMode->getRawHtmlMode();Validation Methods
$safeMode->isUrlSafe('https://example.com'); // true
$safeMode->isUrlSafe('javascript:alert(1)'); // false
$safeMode->isAttributeSafe('class'); // true
$safeMode->isAttributeSafe('onclick'); // false
$safeMode->sanitizeUrl('javascript:alert(1)'); // ''
$safeMode->filterAttributes([
'class' => 'highlight',
'onclick' => 'hack()',
]); // ['class' => 'highlight']Custom Configuration Example
use Djot\DjotConverter;
use Djot\SafeMode;
// Only allow HTTPS links, strip raw HTML
$safeMode = SafeMode::defaults()
->setAllowedSchemes(['https'])
->setRawHtmlMode(SafeMode::RAW_HTML_STRIP);
$converter = new DjotConverter(safeMode: $safeMode);Enabling After Construction
$converter = new DjotConverter();
// Enable later
$converter->setSafeMode(true);
// Or with custom config
$converter->setSafeMode(SafeMode::strict());
// Disable
$converter->setSafeMode(false);Error Handling
The parser can optionally report warnings and errors with line/column information.
Warning Mode
Enable warning collection to detect issues without stopping parsing:
$converter = new DjotConverter(warnings: true);
$html = $converter->convert($djot);
if ($converter->hasWarnings()) {
foreach ($converter->getWarnings() as $warning) {
echo $warning->getMessage(); // "Undefined reference 'foo'"
echo $warning->getLine(); // 5
echo $warning->getColumn(); // 3
// Or as string
echo $warning; // "Undefined reference 'foo' at line 5, column 3"
// Or as array
$data = $warning->toArray();
// ['message' => '...', 'line' => 5, 'column' => 3]
}
}Warnings are reported for:
- Undefined reference links (
[text][missing]) - Undefined footnotes (
[^missing])
Strict Mode
Enable strict mode to throw exceptions on parse errors:
use Djot\Exception\ParseException;
$converter = new DjotConverter(strict: true);
try {
$html = $converter->convert($djot);
} catch (ParseException $e) {
echo $e->getMessage(); // "Unclosed code fence at line 3, column 1"
echo $e->getSourceLine(); // 3
echo $e->getSourceColumn(); // 1
}Errors that throw in strict mode:
- Unclosed code fences
- Unclosed divs (
:::) - Unclosed comments (
{% ... %}) - Unclosed raw blocks
Both Modes
You can enable both modes together - warnings will be collected for non-fatal issues, while fatal errors will throw:
$converter = new DjotConverter(warnings: true, strict: true);ParseWarning
use Djot\Exception\ParseWarning;
$warning->getMessage(): string // Warning message
$warning->getLine(): int // 1-indexed line number
$warning->getColumn(): int // 1-indexed column number
$warning->toArray(): array // ['message' => ..., 'line' => ..., 'column' => ...]
(string)$warning // "Message at line X, column Y"ParseException
use Djot\Exception\ParseException;
$e->getMessage(): string // Full message with location
$e->getSourceLine(): int // 1-indexed line number
$e->getSourceColumn(): int // 1-indexed column numberEvent System
The event system allows you to customize rendering without subclassing.
Event Names
Events are named render.{node_type}:
render.link,render.image,render.heading,render.paragraph, etc.render.*- wildcard, fires for all nodes
Modifying Nodes
Modify node attributes before rendering:
$converter->on('render.link', function (RenderEvent $event): void {
$link = $event->getNode();
// Add target="_blank" to external links
if (str_starts_with($link->getDestination(), 'http')) {
$link->setAttribute('target', '_blank');
$link->setAttribute('rel', 'noopener');
}
});Replacing Output
Replace the rendered HTML entirely:
$converter->on('render.symbol', function (RenderEvent $event): void {
$symbol = $event->getNode();
$event->setHtml(match($symbol->getName()) {
'heart' => '❤️',
'star' => '⭐',
default => ':' . $symbol->getName() . ':',
});
});Chaining
Methods return $this for chaining:
$html = $converter
->on('render.link', $linkHandler)
->on('render.image', $imageHandler)
->convert($djot);RenderEvent
use Djot\Event\RenderEvent;
$event->getNode(): Node // Get the node being rendered
$event->setHtml(string $html) // Replace output HTML
$event->getHtml(): ?string // Get custom HTML if set
$event->preventDefault() // Skip default rendering
$event->isDefaultPrevented(): boolParser Classes
For advanced use cases, you can work directly with the parser and renderer.
BlockParser
Parses Djot input into an AST (Abstract Syntax Tree).
use Djot\Parser\BlockParser;
$parser = new BlockParser(
collectWarnings: false,
strictMode: false,
significantNewlines: false,
);
$document = $parser->parse($djotString);
// Get warnings (if collectWarnings: true)
// Get exception on errors (if strictMode: true)
$warnings = $parser->getWarnings();
// Enable/disable significant newlines mode
$parser->setSignificantNewlines(true);
$isEnabled = $parser->getSignificantNewlines();Custom Block Patterns
Register custom block-level syntax patterns:
$parser = $converter->getParser();
// Register a custom block pattern
$parser->addBlockPattern('/^!!!\\s*(note|warning|danger)\\s*$/', function ($lines, $start, $parent, $p) {
preg_match('/^!!!\\s*(note|warning|danger)\\s*$/', $lines[$start], $m);
$type = $m[1];
// Collect indented content
$content = [];
$i = $start + 1;
while ($i < count($lines) && preg_match('/^\\s+(.*)$/', $lines[$i], $contentMatch)) {
$content[] = $contentMatch[1];
$i++;
}
$div = new Div();
$div->setAttribute('class', 'admonition ' . $type);
$p->parseBlockContent($div, $content); // Parse nested content
$parent->appendChild($div);
return $i - $start; // Return number of lines consumed
});
// Remove a pattern
$parser->removeBlockPattern('/^!!!\\s*(note|warning|danger)\\s*$/');
// List registered patterns
$patterns = $parser->getBlockPatterns();Callback signature:
function(array $lines, int $startIndex, Node $parent, BlockParser $parser): ?int$lines: Array of all input lines$startIndex: Index of the line that matched the pattern$parent: Parent node to append children to$parser: The BlockParser instance (useparseBlockContent()for nested parsing)- Returns: Number of lines consumed, or
nullto fall back to default parsing
Custom Inline Patterns
Register custom inline syntax patterns via the InlineParser:
$inlineParser = $converter->getParser()->getInlineParser();
// Register @mention pattern
$inlineParser->addInlinePattern('/@([a-zA-Z0-9_]+)/', function ($match, $groups, $p) {
$link = new Link('https://example.com/users/' . $groups[1]);
$link->appendChild(new Text('@' . $groups[1]));
return $link;
});
// Remove a pattern
$inlineParser->removeInlinePattern('/@([a-zA-Z0-9_]+)/');
// List registered patterns
$patterns = $inlineParser->getInlinePatterns();Callback signature:
function(string $match, array $groups, InlineParser $parser): ?Node$match: The full matched string$groups: Array of regex capture groups (index 0 is full match)$parser: The InlineParser instance- Returns: A Node to insert, or
nullto fall back to default parsing
Important notes:
- Custom patterns are checked before built-in syntax
- Returning
nullallows the default parser to handle the text - Patterns can override built-in syntax (e.g., override
**bold**)
HtmlRenderer
Renders an AST Document to HTML.
use Djot\Renderer\HtmlRenderer;
$renderer = new HtmlRenderer(xhtml: false);
$html = $renderer->render($document);Configuration:
// Tabs in code blocks default to 4 spaces; use null to preserve tabs
$renderer->setCodeBlockTabWidth(null);
// Customize soft break rendering
$renderer->setSoftBreakMode(SoftBreakMode::Space);
// Enable safe mode for user-generated content
$renderer->setSafeMode(SafeMode::defaults());Tab Width in Code Blocks
By default, tabs in code blocks are converted to 4 spaces. This ensures consistent display across all contexts, since browsers default to 8-space tabs and CSS tab-size isn't supported everywhere (email clients, RSS readers, etc.).
// Preserve tabs instead of converting
$renderer->setCodeBlockTabWidth(null);
// Use different width (e.g., 2 spaces)
$renderer->setCodeBlockTabWidth(2);This affects both fenced code blocks (<pre><code>) and inline code (<code>).
PlainTextRenderer
Renders an AST Document to plain text (useful for search indexing, SEO, email fallbacks).
use Djot\Renderer\PlainTextRenderer;
$renderer = new PlainTextRenderer();
$text = $renderer->render($document);Configuration:
// Customize list item prefix (default: "- ")
$renderer->setListItemPrefix('* ');
// Customize table cell separator (default: "\t")
$renderer->setTableCellSeparator(' | ');Behavior:
- Strips all formatting (emphasis, strong, etc.)
- Preserves text content from links and images (alt text)
- Renders lists with configurable prefixes
- Renders tables with configurable separators
- Strips raw HTML and comments
- Preserves footnote references as
[1]etc.
MarkdownRenderer
Renders an AST Document to CommonMark-compatible Markdown. Useful for:
- Converting Djot content to Markdown for systems that only support Markdown
- Migrating content between formats
- Generating Markdown documentation from Djot source
use Djot\DjotConverter;
use Djot\Renderer\MarkdownRenderer;
$converter = new DjotConverter();
$document = $converter->parse($djotText);
$renderer = new MarkdownRenderer();
$markdown = $renderer->render($document);Conversion Table:
| Djot | Markdown Output |
|---|---|
*strong* | **strong** |
_emphasis_ | *emphasis* |
{-deleted-} | ~~deleted~~ (GFM) |
{+inserted+} | <ins>inserted</ins> |
{=highlighted=} | <mark>highlighted</mark> |
^superscript^ | <sup>superscript</sup> |
~subscript~ | <sub>subscript</sub> |
`code` | `code` |
[text](url) | [text](url) |
 |  |
# Heading | # Heading |
> quote | > quote |
- list | - list |
1. ordered | 1. ordered |
- [ ] task | - [ ] task |
:symbol: | :symbol: |
[^note] | [^note] |
$math$ | $math$ |
$$display$$ | $$display$$ |
| Tables | GFM tables with alignment |
| Divs | Content only (no wrapper) |
| Spans | Content only (no wrapper) |
| Definition lists | Bold term + : description |
| Line blocks | Hard breaks ( \n) |
| Raw HTML | Passed through |
| Comments | Stripped |
Behavior:
- Produces CommonMark-compatible output
- Uses GFM extensions where available (strikethrough, tables, task lists, footnotes)
- Falls back to inline HTML for features without Markdown equivalents
- Escapes special Markdown characters in text content
- Handles nested backticks in code spans and fenced blocks
- Preserves table column alignment
- Normalizes multiple blank lines
Example:
$djot = <<<'DJOT'
# Hello *World*
This has {=highlighted=} and {-deleted-} text.
| Name | Score |
|-------|------:|
| Alice | 95 |
DJOT;
$document = $converter->parse($djot);
$markdown = (new MarkdownRenderer())->render($document);Output:
# Hello **World**
This has <mark>highlighted</mark> and ~~deleted~~ text.
| Name | Score |
| --- | ---: |
| Alice | 95 |Limitations:
- Djot divs (
::: class) lose their class/attributes (content is preserved) - Djot spans (
[text]{.class}) lose their attributes (content is preserved) - Definition lists are approximated (not native Markdown)
- Some whitespace/formatting may differ from original
AST Node Types
Block Nodes
All block nodes extend Djot\Node\Block\BlockNode:
| Class | Description |
|---|---|
Document | Root document node |
Paragraph | Text paragraph |
Heading | Heading (levels 1-6) |
CodeBlock | Fenced code block |
BlockQuote | Block quote |
ListBlock | Ordered, unordered, or task list |
ListItem | List item |
Table | Table |
TableRow | Table row |
TableCell | Table cell (th or td) |
Div | Generic div container |
Section | Section wrapper (used with HeadingPermalinksExtension) |
Figure | Figure container for images/blockquotes with captions |
Caption | Caption for figures and tables |
LineBlock | Line block (preserves line breaks) |
ThematicBreak | Horizontal rule |
DefinitionList | Definition list |
DefinitionTerm | Definition term |
DefinitionDescription | Definition description |
Footnote | Footnote definition |
RawBlock | Raw HTML block |
Comment | Comment (not rendered) |
Inline Nodes
All inline nodes extend Djot\Node\Inline\InlineNode:
| Class | Description |
|---|---|
Text | Plain text |
Emphasis | Emphasized text |
Strong | Strong text |
Code | Inline code |
Link | Hyperlink |
Image | Image |
HardBreak | Hard line break |
SoftBreak | Soft line break |
Span | Span with attributes |
Superscript | Superscript text |
Subscript | Subscript text |
Highlight | Highlighted text |
Insert | Inserted text |
Delete | Deleted text |
Abbreviation | Abbreviation with title (<abbr>) |
FootnoteRef | Footnote reference |
Math | Math expression |
Symbol | Symbol (e.g., :heart:) |
RawInline | Raw HTML inline |
Working with the AST
use Djot\Parser\BlockParser;
use Djot\Renderer\HtmlRenderer;
$parser = new BlockParser();
$renderer = new HtmlRenderer();
// Parse to AST
$document = $parser->parse('# Hello *world*');
// Manipulate AST
foreach ($document->getChildren() as $node) {
echo $node->getType() . "\n"; // "heading"
}
// Render to HTML
$html = $renderer->render($document);
// <h1>Hello <strong>world</strong></h1>Modifying Nodes
// Get/set attributes
$node->setAttribute('class', 'highlight');
$node->getAttribute('class'); // 'highlight'
$node->addClass('special');Node Methods
// Get node type
$node->getType(): string
// Children
$node->getChildren(): array
$node->appendChild(Node $child): void
$node->prependChild(Node $child): void
// Attributes
$node->getAttribute(string $key): mixed
$node->setAttribute(string $key, mixed $value): void
$node->getAttributes(): array
$node->setAttributes(array $attrs): void
$node->addClass(string $class): voidSignificant Newlines Mode
An optional parsing mode for chat messages, comments, and quick notes where markdown-like behavior is more intuitive.
Enabling
// Via factory method (recommended)
$converter = DjotConverter::withSignificantNewlines();
// Via constructor parameter
$converter = new DjotConverter(significantNewlines: true);
// Via parser directly
$parser = new BlockParser(significantNewlines: true);
// Via setter (for runtime switching)
$parser->setSignificantNewlines(true);Behavior Changes
| Feature | Standard Mode | Significant Newlines Mode |
|---|---|---|
| Block elements interrupt paragraphs | No (blank line required) | Yes |
| Nested lists need blank lines | Yes | No |
Note: Soft break rendering is controlled separately via SoftBreakMode - it is not affected by significant newlines mode.
Example
$converter = DjotConverter::withSignificantNewlines();
$djot = <<<'DJOT'
Here is a list:
- item one
- item two
DJOT;
echo $converter->convert($djot);Output:
<p>Here is a list:</p>
<ul>
<li>item one</li>
<li>item two</li>
</ul>In standard mode, the same input would produce:
<p>Here is a list:
- item one
- item two</p>Escaping Block Markers
In significant newlines mode, escape the first character of block markers to keep them literal:
They said:
\> This stays as literal text, not a blockquote
Steps:
\1. This is not an ordered listSoft Break Behavior
Soft break rendering is configured via SoftBreakMode and is independent of significantNewlines mode:
use Djot\DjotConverter;
use Djot\Renderer\SoftBreakMode;
// Set via constructor
$converter = new DjotConverter(softBreakMode: SoftBreakMode::Break);
// Combine with significant newlines for chat-style input
$converter = new DjotConverter(
significantNewlines: true,
softBreakMode: SoftBreakMode::Break,
);Available modes: Newline (default for HTML/Markdown), Space (default for PlainText/ANSI), Break (visible line break). Output varies by renderer - see Parser Options for details.
Feature Support Matrix
This matrix shows which features are supported by each renderer.
Block Elements
| Feature | HtmlRenderer | PlainTextRenderer | MarkdownRenderer | AnsiRenderer |
|---|---|---|---|---|
| Paragraph | ✓ | ✓ | ✓ | ✓ |
| Heading | ✓ | ✓ | ✓ | ✓ (styled) |
| Code Block | ✓ | ✓ | ✓ | ✓ (boxed) |
| Block Quote | ✓ | ✓ (prefixed) | ✓ | ✓ (styled) |
| Lists (ul/ol) | ✓ | ✓ | ✓ | ✓ |
| Task Lists | ✓ | ✓ | ✓ (GFM) | ✓ |
| Tables | ✓ | ✓ (tab-separated) | ✓ (GFM) | ✓ (boxed) |
| Table Captions | ✓ (<caption>) | – | – | ✓ |
| Definition Lists | ✓ | ✓ | ✓ (approximated) | ✓ |
| Divs | ✓ | ✓ (content only) | – (content only) | ✓ |
| Line Blocks | ✓ | ✓ | ✓ (hard breaks) | ✓ |
| Thematic Break | ✓ | ✓ (dashes) | ✓ | ✓ |
| Footnotes | ✓ | ✓ (numbered) | ✓ (GFM) | ✓ |
| Figure/Caption | ✓ | – | – | ✓ |
| Raw HTML | ✓ / escaped | – | ✓ | – |
| Comments | – (stripped) | – | – | – |
Inline Elements
| Feature | HtmlRenderer | PlainTextRenderer | MarkdownRenderer | AnsiRenderer |
|---|---|---|---|---|
| Emphasis | ✓ (<em>) | ✓ (content only) | ✓ (*text*) | ✓ (italic) |
| Strong | ✓ (<strong>) | ✓ (content only) | ✓ (**text**) | ✓ (bold) |
| Code | ✓ (<code>) | ✓ | ✓ (`code`) | ✓ (styled) |
| Links | ✓ | ✓ (text + URL) | ✓ | ✓ (text + URL) |
| Images | ✓ | ✓ (alt text) | ✓ | ✓ (alt text) |
| Superscript | ✓ (<sup>) | ✓ (content only) | ✓ (<sup>) | ✓ (styled) |
| Subscript | ✓ (<sub>) | ✓ (content only) | ✓ (<sub>) | ✓ (styled) |
| Highlight | ✓ (<mark>) | ✓ (content only) | ✓ (<mark>) | ✓ (styled) |
| Insert | ✓ (<ins>) | ✓ (content only) | ✓ (<ins>) | ✓ (styled) |
| Delete | ✓ (<del>) | ✓ (content only) | ✓ (~~text~~) | ✓ (strikethrough) |
| Abbreviation | ✓ (<abbr>) | ✓ (content only) | – | ✓ |
| Spans | ✓ | ✓ (content only) | – (content only) | ✓ (content only) |
| Math | ✓ ($...$) | ✓ (content only) | ✓ ($...$) | ✓ |
| Symbols | ✓ (:name:) | ✓ (content only) | ✓ (:name:) | ✓ (mapped) |
| Footnote Refs | ✓ | ✓ ([n]) | ✓ ([^n]) | ✓ |
| Soft Break | ✓ (configurable) | ✓ | ✓ | ✓ |
| Hard Break | ✓ (<br>) | ✓ (newline) | ✓ ( \n) | ✓ |
| Raw HTML | ✓ / escaped | – | ✓ | – |
Legend
- ✓ — Full support
- ✓ (note) — Supported with noted behavior
- – — Not supported / stripped
Renderer Use Cases
| Renderer | Primary Use Case |
|---|---|
| HtmlRenderer | Web pages, HTML emails, CMS content |
| PlainTextRenderer | Search indexing, SEO descriptions, email fallbacks, accessibility |
| MarkdownRenderer | Converting Djot to CommonMark/GFM for Markdown-only systems |
| AnsiRenderer | Terminal output, CLI tools, console applications |