Markdown
Renders a markdown string (a GFM subset, including pipe tables and safe presentational HTML) as native widgets through the same text pipeline as every other component — deterministic layout, selectable text. source is required and must be one {binding}; the element takes no children. Applications can pass already-loaded image mappings through images, links dispatch on-link with the URL as payload (bare URLs autolink), <details> blocks toggle through on-details plus a model-owned details-expanded flag list, and #123 references linkify through issue-link-base. Fenced blocks lower through the reusable Code component, so indentation and syntax behavior stay identical.

Markup
<markdown source="{release_notes}" images="{markdown_images}" on-link="open_link" issue-link-base="https://github.com/native-sdk/native/issues/"></markdown>Programmatic construction (Zig)
The builder is the canvas.markdown module, parameterized over the app's Msg type; on_link pairs with Ui.linkMsg(.tag).
const Md = native_sdk.canvas.markdown.Markdown(Msg);
Md.view(ui, model.release_notes, .{
.images = model.markdown_images,
.on_link = Ui.linkMsg(.open_link),
.issue_link_base = "https://github.com/native-sdk/native/issues/",
})Image discovery writes canonical, entity-decoded URLs into caller-owned bounded storage. Consume each value() while that storage is alive, copy accepted sources into the model, and use the same bytes for both fx.loadImage and the eventual ResolvedImage.source mapping:
var source_storage: [canvas.markdown.max_markdown_images]canvas.markdown.CollectedImageSource = undefined;
const sources = canvas.markdown.collectImageSources(model.release_notes, &source_storage);
for (sources) |*collected| {
const source = collected.value();
// Validate the scheme, copy source into the model, then issue fx.loadImage.
}HTML subset
README- and comment-style HTML is lowered onto native presentation rather than passed to a browser:
- Text:
<b>,<strong>,<i>,<em>,<var>,<s>,<strike>,<del>,<u>,<ins>,<code>,<kbd>,<samp>,<tt>,<mark>,<small>,<sub>, and<sup>. - Content:
<a href>,<img src alt width height>,<br>,<wbr>,<q>, HTML comments, core named entities, and numeric entities. A leading image in a paragraph, heading, list item, blockquote, or pipe-table cell becomes a native image whenimagescontains a successfulcanvas.markdown.ResolvedImagemapping for its source; unresolved and mid-paragraph images fall back to alt text. The view never fetches remote media — discover bounded sources withcanvas.markdown.collectImageSources, read each caller-ownedCollectedImageSourcethroughvalue(), load it throughfx.loadImage, retain successful ids and dimensions in the model, and pass mappings with that canonical source back on the next rebuild. - Blocks and wrappers:
<h1>through<h6>,<p>,<blockquote>,<pre>,<hr>, list items, and the common<div align="center">README pattern. Harmless list, table, and container wrappers are accepted as readable native content; use Markdown pipe tables for full native table layout.<details>and<summary>retain their controlled expansion behavior.
This is a sanitized native subset, not a DOM. CSS, scripts, forms, embeds, event attributes, and arbitrary HTML do not execute; unsupported or malformed tags remain literal text. Only href, image src/alt/dimensions, and block align affect presentation.
Attributes
| Attribute | Description |
|---|---|
source | markdown: one {binding} producing the markdown text (a []const u8 field or fn; arena fns work). Required. |
images | markdown: one {binding} producing []const canvas.markdown.ResolvedImage (arena fns work). Each mapping pairs a leading image source with an image id already loaded and registered through fx.loadImage plus its decoded dimensions; views never perform image I/O. |
on-link | markdown: bare Msg tag dispatched on link press; its payload is the URL ([]const u8 variant). |
on-details | markdown: bare Msg tag dispatched on a <details> summary press; its payload is the block index (usize variant). |
details-expanded | markdown: {binding} naming a []const bool iterable of expanded flags, in details-block document order. |
issue-link-base | markdown: literal URL prefix or one {binding}; '#123' refs become links to base ++ number (ghissue:// or https://github.com/owner/repo/issues/). |
