Logo

Component overrides

React and Vue can replace elements that originate in Markdown with framework-native components. Parsing, sanitization, structural stabilization, scheduling, table shells, image readiness, and fenced-code behavior remain owned by Smoothstream.

import {  Smoothstream,  type SmoothstreamComponents,} from "@smoothstream/react"; const components: SmoothstreamComponents = {  h2: ({ children, className, ...props }) => (    <h2 {...props} className={`app-heading ${className ?? ""}`}>      {children}    </h2>  ),  a: ({ children, className, ...props }) => (    <a      {...props}      className={`app-link ${className ?? ""}`}    >      {children}    </a>  ),}; <Smoothstream components={components} receiving={receiving}>  {markdown}</Smoothstream>
<script setup lang="ts">import { Smoothstream } from "@smoothstream/vue";import AppHeading from "./AppHeading.vue";import AppLink from "./AppLink.vue"; const components = {  h2: AppHeading,  a: AppLink,};</script> <template>  <Smoothstream    :components="components"    :markdown="markdown"    :receiving="receiving"  /></template>

A Vue override receives its content through the default slot. Let fallthrough attributes reach its root element, or forward $attrs explicitly when inheritAttrs is disabled.

Available components

CategoryNames
Headingsh1, h2, h3, h4, h5, h6
Textp, strong, em, del
Listsul, ol, li
Linksa
CodeinlineCode
Quotesblockquote
Tablestable, thead, tbody, tr, th, td
Mediaimg
Otherhr, br

inlineCode replaces Markdown code spans such as `npm install`. Fenced code is deliberately not part of this API because Smoothstream owns its line admission, highlighting, language label, copy control, and streaming presentation.

Task-list checkboxes, the Markdown root, reveal spans, table scroll wrappers, image state, code controls, syntax tokens, and accessibility announcers are also internal rather than override targets.

Forward the supplied properties

Overrides receive their rendered children and the sanitized properties of the original element, including href, src, alt, classes, accessibility attributes, and Smoothstream’s functional data-* attributes and styles. Confirmed links supply their href and title from the first render, while their labels are still animating. Render the supplied children without converting them to text, and forward properties you do not intentionally replace.

The default theme continues to apply when an override ultimately renders the same semantic element and forwards its properties. If it changes the element structure, it owns the resulting semantics and styling.

Group children in flex and grid layouts

During a reveal, children can temporarily contain multiple elements representing the text being presented. If an override places those children directly in a flex or grid container, each temporary element can become a separate layout item. Properties such as gap may then add space between characters or words until the content settles.

Group the streamed children in a semantically appropriate element when the override uses direct-child layout or adds sibling UI:

const Heading = ({ children, className, ...props }) => (  <h2    {...props}    className={`app-heading ${className ?? ""}`}  >    <span className="app-heading-content">      {children}    </span>    <span aria-hidden="true" className="app-heading-badge">      Custom    </span>  </h2>);
<template>  <h2 v-bind="$attrs" class="app-heading">    <span class="app-heading-content">      <slot />    </span>    <span aria-hidden="true" class="app-heading-badge">      Custom    </span>  </h2></template>
.app-heading {  display: flex;  align-items: baseline;  gap: 0.5rem;} .app-heading-content {  min-width: 0;}

The wrapper becomes one flex or grid item while Smoothstream’s reveal elements remain inside it. It is unnecessary for overrides that keep normal inline or block flow. Choose a wrapper that is valid for the element being rendered: a span works for heading and other phrasing content, but must not be inserted directly beneath structural elements such as ul, ol, table, thead, tbody, or tr. The extra wrapper also changes direct-child selectors such as h2 > a, so add it only where the layout needs it.

Keep configuration stable

Changing components does not reparse or reschedule Markdown, but replacing a component type can remount that rendered subtree. Define the map outside render or memoize it when possible, and treat it as per-response configuration.

Both adapters export SmoothstreamComponentName and SmoothstreamComponents; React also exports SmoothstreamComponentProps<Name>. Smoothstream does not expose its HAST nodes through component props; the adapter-facing syntax tree remains an internal implementation detail.

The vanilla DOM adapter has no component runtime and does not accept components. Use semantic CSS selectors and unstyled with that adapter.

© 2026 Smoothstream. All rights reserved.

Built withRadiant Docs