MarkdownView
SignalX-native, streaming-aware markdown renderer. A Lynx component map over the @sigx/richtext render engine — identical on iOS, Android and Harmony, with no native module.
Import
import { MarkdownView } from '@sigx/lynx-markdown';
MarkdownView lives on the package root, which pulls in no runtime peers beyond @sigx/lynx — renderer-only consumers have nothing to prebuild.
Basic usage
value is the markdown source and the only prop you usually need. It is reactive: pass a signal's value and the view re-renders as the source grows.
import { MarkdownView } from '@sigx/lynx-markdown';
function Doc() {
return (
<MarkdownView value={'# Hello\n\nThis is **markdown** rendered to native views.'} />
);
}
Supports CommonMark + GFM: headings, emphasis, strong, strikethrough, inline code, links and reference links, images, autolinks, nested ordered and unordered lists, task lists, blockquotes, fenced and indented code, thematic breaks, and tables with per-column alignment. Raw HTML renders as literal text.
Handling link and image taps
The default renderers never navigate on their own — wire onLink and onImageTap to act on taps. URLs are sanitised by the engine first: only http(s):, mailto:, tel: and scheme-less links reach onLink; javascript: and friends become #.
import { MarkdownView } from '@sigx/lynx-markdown';
function Article(props: { source: string }) {
return (
<MarkdownView
value={props.source}
onLink={(url) => openInBrowser(url)}
onImageTap={(url) => showLightbox(url)}
/>
);
}
The default image renderer shows the alt text (or the URL) as a tappable label, not a real <image> — supply your own image component via components for inline pictures.
Streaming AI output
Pair MarkdownView with createTextStream to render a token loop flicker-free: finalized blocks keep their identity and keys and are never re-parsed or remounted, so completed paragraphs and code blocks never reflow.
import { MarkdownView, createTextStream } from '@sigx/lynx-markdown';
const stream = createTextStream({ flushIntervalMs: 16 });
async function generate(prompt: string) {
stream.reset();
for await (const token of askModel(prompt)) stream.append(token);
stream.done();
}
function ChatBubble() {
return <MarkdownView value={stream.value.value} />;
}
Theming with a component map
MarkdownView is design-system agnostic. It ships neutral defaultComponents and merges your partial components map over them — any slot you omit falls back to the default. Slots are keyed by mdast node type; block components return a JSXElement, inline components may return a string.
import { MarkdownView, type LynxMarkdownComponents } from '@sigx/lynx-markdown';
const components: Partial<LynxMarkdownComponents> = {
heading: ({ depth, children }) => (
<text style={{ fontSize: depth === 1 ? 28 : 20, fontWeight: 'bold' }}>{children}</text>
),
strong: ({ children }) => <text style={{ fontWeight: 'bold', color: '#7c3aed' }}>{children}</text>,
};
function Themed(props: { source: string }) {
return <MarkdownView value={props.source} components={components} />;
}
For a ready-made theme, @sigx/lynx-daisyui/markdown ships markdownComponents — a complete daisyUI-styled map you can pass straight to components.
Plugins
plugins takes @sigx/richtext plugins: node specs, and markdown syntax under formats.markdown. A plugin node renders through components[node.type]. To show @[label](id) mentions as chips:
import { MarkdownView, mentionPlugin, type Mention } from '@sigx/lynx-markdown';
const plugins = [mentionPlugin];
const components = { mention: ({ node }: { node: Mention }) => <text class="chip">@{node.label}</text> };
function Transcript(props: { source: string }) {
return <MarkdownView value={props.source} plugins={plugins} components={components} />;
}
Keep
pluginsa stable array reference (a module constant, not an inline literal). Changing its identity recreates the parse engine and re-parses from scratch, defeating incremental streaming.
Props
All props are optional.
| Prop | Type | Description |
|---|---|---|
value | string | The markdown source. Reactive and parsed incrementally. |
onLink | (url: string, node) => void | Called with the sanitised URL and the link node when a link is tapped. |
onImageTap | (url: string) => void | Called with the image URL when an image is tapped. |
components | Partial<LynxMarkdownComponents> | Per-slot render overrides, merged over defaultComponents. |
plugins | readonly RichTextPlugin[] | Stable array of @sigx/richtext plugins (e.g. mentionPlugin). |
See the API reference for the component-map types, and Rendering for every slot's props.
