Markdown
Markdown renders formatted text using markdown syntax. Use Text for simple, styled text content, and Markdown when you need rich formatting.
Key features:
- Rich formatting: Support for headings, bold, italic, lists, links, images, blockquotes, and code blocks
- Dynamic content: Use @{} binding expressions to inject variables and function results
- File loading: Load Markdown content from external files using the
dataproperty - HTML: Use a subset of HTML directly in Markdown
Acquiring content
You can specify Markdown content in these ways.
The content property
Render Markdown content that you calculate or get from other components.
The data property
Render Markdown content from an URL.
Nested text
Render Markdown content that you place directly in a Markdown component.
Whitespace and special characters
Whitespace is significant in Markdown, for example headers using the # syntax must begin in column 1.
These special XML characters are significant too.
< (less than) - Must be escaped as <
> (greater than) - Must be escaped as >
& (ampersand) - Must be escaped as &
" (double quote) - Must be escaped as " in attributes
' (single quote/apostrophe) - Must be escaped as ' in attributes
You can use a CDATA section to avoid having to escape these characters individually.
<Markdown>
<![CDATA[
]]>
</Markdown>
Or, as we have done in this page, you can use a code fence (a block delimited by triple backtics) to preserve them.
Supported elements
The Markdown component supports these basic elements.
- Heading
- Bold
- Italic
- Strikethrough
- Blockquote
- Ordered List
- Unordered List
- Code
- Horizontal Rule
- Link
- Image
- Table
See this markdown guide.
Native HTML
Markdown allows a subset of HTML. For example, while Markdown itself does not support rowspan and colspan in tables, you can use HTML directly.
<App>
<Markdown>
<![CDATA[
<table>
<thead>
<tr>
<th colspan="2">Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr>
<td>Jill</td>
<td>Smith</td>
<td>43</td>
</tr>
<tr>
<td>Eve</td>
<td>Jackson</td>
<td>57</td>
</tr>
</tbody>
</table>
]]>
</Markdown>
</App><App>
<Markdown>
<![CDATA[
<table>
<thead>
<tr>
<th colspan="2">Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr>
<td>Jill</td>
<td>Smith</td>
<td>43</td>
</tr>
<tr>
<td>Eve</td>
<td>Jackson</td>
<td>57</td>
</tr>
</tbody>
</table>
]]>
</Markdown>
</App>Binding Expressions
Our Markdown component is capable of evaluating binding expressions just as other XMLUI components.
Use the @{} syntax to wrap expressions that need to be evaluated.
Objects, functions and arrays will be stringified if you place them in Markdown.
Function calls are executed and their return values inlined as strings into markdown.
XMLUI playground apps
xmlui-pg fences accept the same entrypoint format as Main.xmlui.
The fence can contain just the app markup, or it can also contain top-level
<Component> declarations that are reusable inside that app.
<Component name="StatusPill">
<Badge value="{$props.value}" variant="pill" />
</Component>
<App>
<VStack gap="8px">
<StatusPill value="Ready" />
<StatusPill value="Synced" />
</VStack>
</App><Component name="StatusPill">
<Badge value="{$props.value}" variant="pill" />
</Component>
<App>
<VStack gap="8px">
<StatusPill value="Ready" />
<StatusPill value="Synced" />
</VStack>
</App>An entrypoint may contain zero, one, or many top-level <Component> declarations,
but it may contain only one top-level non-Component app root. The declarations
and app root can appear in any order. If it contains only <Component>
declarations, XMLUI renders an empty Fragment and logs a browser warning.
Component files and ---comp playground sections remain strict component
definitions; if a strict component section or component file has the same name as
an inline entrypoint component, the strict component definition wins.
Context variables available during execution:
$anchorHref: The href (#id) of the current heading anchor.$anchorId: The generated id of the current heading anchor.
Behaviors
This component supports the following behaviors:
| Behavior | Properties |
|---|---|
| Animation | animation, animationOptions |
| Bookmark | bookmark, bookmarkLevel, bookmarkTitle, bookmarkOmitFromToc |
| Component Label | label, labelPosition, labelWidth, labelBreak, required, enabled, shrinkToLabel, style, readOnly |
| Tooltip | tooltip, tooltipMarkdown, tooltipOptions |
| Styling Variant | variant |
Properties
allowHtml
default: true
When true (default), a subset of raw HTML embedded in the content is rendered as real elements. Set this to false for content that arrives at runtime as data so that raw HTML tags render as literal text instead of markup — a quoted <table> shows its tags rather than building a table. Only the HTML-tag interpretation is affected; markdown formatting, code fences, and inline code are untouched. Pair with interpolateBindings="false" for a fully data-safe render.
By default, Markdown renders a subset of raw HTML embedded in the content as real elements — a <table> in the text builds a table. That is fine for markup you author, but for content that arrives at runtime as data (transcripts, logs, user input) any quoted HTML would render as live markup and can break layout.
Set allowHtml="false" so raw HTML tags render as literal text instead. Only the HTML-tag interpretation is neutralized — markdown formatting, code fences, and inline code are untouched, and an unterminated tag survives verbatim rather than being dropped.
This axis is independent of interpolateBindings. Pair the two (interpolateBindings="false" allowHtml="false") for a fully data-safe render: nothing is evaluated, nothing is rewritten, and no HTML is activated.
<App var.dataLine="{'Rendered row: <tr><td>oops</td></tr> — and a bare <script> tag'}">
<VStack gap="8px">
<Text variant="strong">allowHtml="true" (default)</Text>
<Markdown content="{dataLine}" />
<Text variant="strong">allowHtml="false"</Text>
<Markdown allowHtml="false" content="{dataLine}" />
</VStack>
</App><App var.dataLine="{'Rendered row: <tr><td>oops</td></tr> — and a bare <script> tag'}">
<VStack gap="8px">
<Text variant="strong">allowHtml="true" (default)</Text>
<Markdown content="{dataLine}" />
<Text variant="strong">allowHtml="false"</Text>
<Markdown allowHtml="false" content="{dataLine}" />
</VStack>
</App>anchorTemplate
An optional template to customize the anchor link rendered next to each heading. Requires showHeadingAnchors to be true. The template receives $anchorId and $anchorHref as context variables.
breakMode
default: "normal"
This property controls how text breaks into multiple lines. normal uses standard word boundaries, word breaks long words to prevent overflow, anywhere breaks at any character, keep prevents word breaking, and hyphenate uses automatic hyphenation. When not specified, uses the default browser behavior or theme variables.
Available values:
| Value | Description |
|---|---|
normal | Uses standard word boundaries for breaking (default) |
word | Breaks long words when necessary to prevent overflow |
anywhere | Breaks at any character if needed to fit content |
keep | Prevents breaking within words entirely |
hyphenate | Uses automatic hyphenation when breaking words |
<App>
<VStack gap="16px">
<VStack gap="8px">
<Text variant="strong">breakMode="normal" (default)</Text>
<Markdown
width="200px"
backgroundColor="lightblue"
padding="8px"
breakMode="normal">
<![CDATA[
This text uses standardwordbreaking at natural boundaries like spaces and hyphens.
]]>
</Markdown>
</VStack>
<VStack gap="8px">
<Text variant="strong">breakMode="word"</Text>
<Markdown
width="200px"
backgroundColor="lightgreen"
padding="8px"
breakMode="word">
<![CDATA[
This text will breakverylongwordswhenneeded to prevent overflow while preserving readability.
]]>
</Markdown>
</VStack>
<VStack gap="8px">
<Text variant="strong">breakMode="anywhere"</Text>
<Markdown
width="200px"
backgroundColor="lightyellow"
padding="8px"
breakMode="anywhere">
<![CDATA[
Thistext willbreakanywhereif neededtofit thecontainer eveninthe middleofwords.
]]>
</Markdown>
</VStack>
</VStack>
</App><App>
<VStack gap="16px">
<VStack gap="8px">
<Text variant="strong">breakMode="normal" (default)</Text>
<Markdown
width="200px"
backgroundColor="lightblue"
padding="8px"
breakMode="normal">
<![CDATA[
This text uses standardwordbreaking at natural boundaries like spaces and hyphens.
]]>
</Markdown>
</VStack>
<VStack gap="8px">
<Text variant="strong">breakMode="word"</Text>
<Markdown
width="200px"
backgroundColor="lightgreen"
padding="8px"
breakMode="word">
<![CDATA[
This text will breakverylongwordswhenneeded to prevent overflow while preserving readability.
]]>
</Markdown>
</VStack>
<VStack gap="8px">
<Text variant="strong">breakMode="anywhere"</Text>
<Markdown
width="200px"
backgroundColor="lightyellow"
padding="8px"
breakMode="anywhere">
<![CDATA[
Thistext willbreakanywhereif neededtofit thecontainer eveninthe middleofwords.
]]>
</Markdown>
</VStack>
</VStack>
</App>content
This property sets the markdown content to display. Alternatively, you can nest the markdown content as a child in a CDATA section. In neither this property value nor any child is defined, empty content is displayed.
Use this property when the text you provide is not static but a result of calculations (you assemble the text or get it from other components).
grayscale
This boolean property specifies whether images should be displayed in grayscale. If set to true, all images within the markdown will be rendered in grayscale.
highlightActive
When true, this Markdown block holds the active match: its first highlightText occurrence is emphasized and scrolled into view.
highlightActiveIndex
Which occurrence (0-based) of highlightText is the active match: it is emphasized and scrolled into view. Occurrences are counted across all terms in document order, so stepping the index walks every <mark> top-to-bottom regardless of which term produced it. -1 or unset means none. Generalizes highlightActive.
highlightText
When set, wraps every case-insensitive occurrence in the rendered content in a <mark> element (highlighted). Accepts a string (a single phrase) or a string array (each term highlighted independently). Works across prose, code, and links. A term shorter than 2 characters, an empty string, or an empty array is a no-op.
interpolateBindings
default: true
When true (default), the content is treated as authored markup: @{...} binding expressions are evaluated and replaced with their values, and xmlui-pg playground fences and tree-display blocks are rendered as live examples. Set this to false for content that arrives at runtime as data (transcripts, logs, user text) so that @{...} sequences — which collide with real-world syntax such as PowerShell hashtable literals (@{ ... }) — render literally instead of being evaluated, and a quoted xmlui-pg fence renders as a code block instead of being rewritten into a live playground.
By default, Markdown evaluates @{...} binding expressions in its content. That is convenient for text you author, but risky for text that arrives at runtime as data — transcripts, logs, or user input.
The collision is a bare @ immediately followed by {, which is common in real code and markup: PowerShell hashtables (@{ LogName = "System" }), Razor/Blazor code blocks (@{ ... }), Objective-C dictionary literals (@{ @"k": v }), Perl dereferences (undefined), and LaTeX column specs (@{...}). When such text is evaluated as a binding it produces wrong output or an error — and an empty `` (e.g. a LaTeX inter-column spec) is silently removed rather than shown.
Set interpolateBindings="false" for data-fed content so every @{...} sequence renders literally:
<App var.logLine="{'Get-WinEvent -FilterHashtable @{ LogName = System; Id = 3077 }'}">
<Markdown interpolateBindings="false" content="{logLine}" />
</App><App var.logLine="{'Get-WinEvent -FilterHashtable @{ LogName = System; Id = 3077 }'}">
<Markdown interpolateBindings="false" content="{logLine}" />
</App>openLinkInNewTab
This boolean property specifies whether links should open in a new tab. If set to true, all links within the markdown will open in a new tab with target="_blank". Links that explicitly specify their own target using the | target=... syntax will override this setting.
overflowMode
default: "not specified"
This property controls how text overflow is handled. none prevents wrapping and shows no overflow indicator, ellipsis shows ellipses when text is truncated, scroll forces single line with horizontal scrolling, and flow allows multi-line wrapping with vertical scrolling when needed. When not specified, uses the default text behavior.
Available values:
| Value | Description |
|---|---|
none | No wrapping, text stays on a single line with no overflow indicator |
ellipsis | Truncates with an ellipsis |
scroll | Forces single line with horizontal scrolling when content overflows |
flow | Allows text to wrap into multiple lines with vertical scrolling when container height is constrained |
<App>
<VStack gap="16px">
<VStack gap="8px">
<Text variant="strong">overflowMode="flow"</Text>
<Markdown
width="300px"
backgroundColor="lightblue"
padding="8px"
overflowMode="flow">
<, all wrapping as needed.
When you have comma-separated lists like: [reference-1](url1), [reference-2](url2), [reference-3](url3), [reference-4](url4), they will wrap appropriately.
]]>
</Markdown>
</VStack>
<VStack gap="8px">
<Text variant="strong">overflowMode="scroll"</Text>
<Markdown
width="300px"
backgroundColor="lightgreen"
padding="8px"
overflowMode="scroll">
<![CDATA[
This text stays on a single line with horizontal scrolling when content overflows the container width.
]]>
</Markdown>
</VStack>
<VStack gap="8px">
<Text variant="strong">overflowMode="ellipsis"</Text>
<Markdown
width="300px"
backgroundColor="lightyellow"
padding="8px"
overflowMode="ellipsis">
<![CDATA[
This text truncates with ellipsis when it exceeds the container width.
]]>
</Markdown>
</VStack>
</VStack>
</App><App>
<VStack gap="16px">
<VStack gap="8px">
<Text variant="strong">overflowMode="flow"</Text>
<Markdown
width="300px"
backgroundColor="lightblue"
padding="8px"
overflowMode="flow">
<, all wrapping as needed.
When you have comma-separated lists like: [reference-1](url1), [reference-2](url2), [reference-3](url3), [reference-4](url4), they will wrap appropriately.
]]>
</Markdown>
</VStack>
<VStack gap="8px">
<Text variant="strong">overflowMode="scroll"</Text>
<Markdown
width="300px"
backgroundColor="lightgreen"
padding="8px"
overflowMode="scroll">
<![CDATA[
This text stays on a single line with horizontal scrolling when content overflows the container width.
]]>
</Markdown>
</VStack>
<VStack gap="8px">
<Text variant="strong">overflowMode="ellipsis"</Text>
<Markdown
width="300px"
backgroundColor="lightyellow"
padding="8px"
overflowMode="ellipsis">
<![CDATA[
This text truncates with ellipsis when it exceeds the container width.
]]>
</Markdown>
</VStack>
</VStack>
</App>For comma-separated markdown links (common in reference lists), use overflowMode="flow" with optional breakMode="word":
<App>
<Markdown
width="400px"
backgroundColor="lavender"
padding="8px"
overflowMode="flow"
breakMode="word">
<, [PR #456](https://example.com/pr/456), [issue #789](https://example.com/issue/789), [PR #1011](https://example.com/pr/1011), [issue #1213](https://example.com/issue/1213)
]]>
</Markdown>
</App><App>
<Markdown
width="400px"
backgroundColor="lavender"
padding="8px"
overflowMode="flow"
breakMode="word">
<, [PR #456](https://example.com/pr/456), [issue #789](https://example.com/issue/789), [PR #1011](https://example.com/pr/1011), [issue #1213](https://example.com/issue/1213)
]]>
</Markdown>
</App>removeBr
default: false
This boolean property specifies whether <br> (line break) elements should be omitted from the rendered output. When set to true, <br/> tags in the markdown content will not be rendered. When false (default), <br/> tags render as horizontal bars.
removeIndents
default: true
This boolean property specifies whether leading indents should be removed from the markdown content. If set to true, the shortest indent found at the start of the content lines is removed from the beginning of every line.
<App layout="horizontal-sticky" padding="1rem">
<Markdown removeIndents="true">
<![CDATA[
# My Adventure in Markdown Land
## The Beginning
In the bustling city of Markdownville, I embarked on a journey to
discover the secrets of Markdown. My adventure started in the heart
of the city, where the first rule of Markdown was inscribed in stone.
]]>
</Markdown>
</App><App layout="horizontal-sticky" padding="1rem">
<Markdown removeIndents="true">
<![CDATA[
# My Adventure in Markdown Land
## The Beginning
In the bustling city of Markdownville, I embarked on a journey to
discover the secrets of Markdown. My adventure started in the heart
of the city, where the first rule of Markdown was inscribed in stone.
]]>
</Markdown>
</App>showHeadingAnchors
This boolean property specifies whether heading anchors should be displayed. If set to true, heading anchors will be displayed on hover next to headings.
If this property is not set, the engine checks if the showHeadingAnchors flag is turned on in xmluiConfig and displays the heading anchor accordingly.
truncateLinks
This boolean property specifies whether long links should be truncated with ellipsis. If set to true, links will be displayed with a maximum width and overflow will be hidden with text-overflow: ellipsis.
<App>
<Markdown truncateLinks="true">
<![CDATA[
This is a long link truncated for display: https://playground.xmlui.org/#/playground/#H4sIAAAAAAAAE1VSS2vjMBD%2BK2LIYRdsy7u0F%2BEGlvbYPSXspQmLYo0dUXskpHHqNvi%2FL1Lchd7m9T1mmCtE1mT04AhBXUF7DwqaX95vDyRE82wjC6NZPxzgzOyjklJ7W3E3VL27VNOrHCyhHJ1BydMJZWTNUzxAxgshmj3OvL1uLONYkR5xUWLNEnKXxzG%2B1MfqBt3hBYPl9yeMbbCeraOlkZklW5LJ0%2FZAjby5hAJaN3pHSBxBvRxTTp3t0z5JEBQ8OzKOxH46obgpQgF8xhET4grWgIJ5HCZbGtemJs6MZOJnOYsMLoCCTQ5KH%2Byow3t5X9efXH90iEm0c8Q7%2B5GEf9z5GQo46fa1D24i85jBaZuydSaNhP70rQtuFCtxnEKnWyx%2F1rUIohcnIUVd3X2HArw2xlL%2FBb6JPk3Xf%2B9hWY7LUoDLN7s5sTOaffIGqtNDxALim%2FYezf%2FcB7xYfPud2daaCxaJdaIBBWcX7Icj1gMUoFu2F1wpv55sbeVPgsH2Z85XI0ZiUPm1luUfOQ4%2BonECAAA%3D
]]>
</Markdown>
</App><App>
<Markdown truncateLinks="true">
<![CDATA[
This is a long link truncated for display: https://playground.xmlui.org/#/playground/#H4sIAAAAAAAAE1VSS2vjMBD%2BK2LIYRdsy7u0F%2BEGlvbYPSXspQmLYo0dUXskpHHqNvi%2FL1Lchd7m9T1mmCtE1mT04AhBXUF7DwqaX95vDyRE82wjC6NZPxzgzOyjklJ7W3E3VL27VNOrHCyhHJ1BydMJZWTNUzxAxgshmj3OvL1uLONYkR5xUWLNEnKXxzG%2B1MfqBt3hBYPl9yeMbbCeraOlkZklW5LJ0%2FZAjby5hAJaN3pHSBxBvRxTTp3t0z5JEBQ8OzKOxH46obgpQgF8xhET4grWgIJ5HCZbGtemJs6MZOJnOYsMLoCCTQ5KH%2Byow3t5X9efXH90iEm0c8Q7%2B5GEf9z5GQo46fa1D24i85jBaZuydSaNhP70rQtuFCtxnEKnWyx%2F1rUIohcnIUVd3X2HArw2xlL%2FBb6JPk3Xf%2B9hWY7LUoDLN7s5sTOaffIGqtNDxALim%2FYezf%2FcB7xYfPud2daaCxaJdaIBBWcX7Icj1gMUoFu2F1wpv55sbeVPgsH2Z85XI0ZiUPm1luUfOQ4%2BonECAAA%3D
]]>
</Markdown>
</App>Events
This component does not have any events.
Exposed Methods
This component does not expose any methods.
Styling
The component itself cannot be styled, but the components that render the final text have customizable style variables.
Text
Heading
Link
Image
Checkbox