shikidown
Guides

MarkdownComponent

The <shikidown> component — inputs, rendering states, and per-instance component registration.

MarkdownComponent is the default way to display a Markdown document. It is standalone, uses OnPush change detection, and is the only integration point that supports incremental rendering.

import { MarkdownComponent } from 'shikidown';

@Component({
  imports: [MarkdownComponent],
  template: `<shikidown [content]="md" class="prose dark:prose-invert max-w-none" />`,
})
export class MyComponent {
  readonly md = '# Hello';
}

The host element carries class="block", and any class you add lands beside it — which is where your typography goes, since the library ships no styles.

Inputs

Prop

Type

There are no outputs.

Rendering states

The component renders one of four things, in this order:

Loading — a pulsing skeleton, shown only on the first render. Once a document has been rendered, subsequent updates keep the previous output on screen rather than falling back to the skeleton.

Error — a red panel carrying the message, if parsing threw.

Incremental output — one <div class="shikidown-block"> per root block, tracked by a stable id; the block being written carries data-streaming.

Standard output — a single <div [innerHTML]> holding the whole document.

The skeleton and error panel are styled with Tailwind utility classes (animate-pulse, bg-gray-200, text-red-700…). Without Tailwind they still render — as unstyled boxes.

Per-instance component registration

Components passed through [components] are registered for the browser globally — there is no such thing as a scoped Custom Element — but you only have to declare them where they are used:

<shikidown [content]="md" [components]="{ 'local-chart': ChartComponent }" />

customElements.define() is called at most once per selector, however many instances mount, so repeating a selector across templates is harmless.

The trade-offs between declaring components here and declaring them in provideMarkdown() are laid out in Embedding Angular components.

Content security

The rendered HTML is not passed through Angular's DomSanitizer, which would strip the very Custom Elements the library exists to render, along with Shiki's inline colour styles. Raw HTML in the Markdown is instead filtered by shikidown's own allow-list (htmlPolicy: 'sanitize', the default) — see Content security.

htmlPolicy: 'trusted' restores the 2.x behaviour, where raw HTML reaches the DOM untouched. Never use it for LLM output or user-submitted text.

Server-side rendering

Custom Element registration is guarded by isPlatformBrowser, and Shiki is only imported in the browser. On the server the document renders with its structure intact but without highlighting and without live components; both appear on hydration.

See also

On this page