Lynx/Modules/Markdown/MarkdownView
@sigx/lynx-markdown · Stable · Component library

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#

TSX
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.

TSX
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.

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 #.

TSX
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.

TSX
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.

TSX
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:

TSX
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 plugins a 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.

PropTypeDescription
valuestringThe markdown source. Reactive and parsed incrementally.
onLink(url: string, node) => voidCalled with the sanitised URL and the link node when a link is tapped.
onImageTap(url: string) => voidCalled with the image URL when an image is tapped.
componentsPartial<LynxMarkdownComponents>Per-slot render overrides, merged over defaultComponents.
pluginsreadonly 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.