Skip to main content

Writing & markdown

Backroad has three primitives for emitting text into the page: br.write, br.title, and br.link / br.linkGroup.

br.write — markdown

The workhorse. Pass any markdown string and Backroad renders it with GitHub-flavored markdown (headings, bold, lists, code fences, tables, blockquotes, inline HTML).

br.write({
body: `
# Hello

This is **bold**, *italic*, and \`inline code\`.

- one
- two
- three

\`\`\`ts
const greeting = 'hi';
\`\`\`
`,
});

body can also be a number, in which case it renders as a plain string.

Streaming markdown

br.write appends a new block on every call. To grow a single block over time — streamed LLM output, a live log, incremental progress — use writeStream or streamable, which create one markdown node and re-render it in place rather than emitting new siblings:

// Pump an async iterable of text chunks; returns the full string when done.
const full = await br.writeStream(tokenStream);

// Or drive it by hand:
const { update } = br.streamable();
update('Working…');
update('Working… done ✅');

Most commonly used for chat — see LLM components.

br.title — quick page heading

A shortcut for br.write({ body: '# ...' }) that's slightly less noisy when you just want a top-of-page title:

br.title({ label: 'Dashboard' });

For one link:

br.link({ label: 'Docs', href: '/docs', target: '_blank' });

For a nav-style row:

br.linkGroup({
items: [
{ label: 'Home', href: '/' },
{ label: 'Docs', href: '/docs' },
{
label: 'GitHub',
href: 'https://github.com/sudomakes/backroad',
target: '_blank',
},
],
});

Building markdown from values

Because body is just a string, you can interpolate any state from earlier in the script:

const name = br.textInput({ label: 'Name', defaultValue: 'world' });
br.write({ body: `Hello, **${name}**!` });

Try it