Logo

Troubleshooting

Start with the symptom you see. Most integration issues come from the lifetime of one response, the meaning of receiving, or styles applied to temporary reveal content.

The final word or block never appears

Check that receiving becomes false when the request completes, fails, or is cancelled. An open input can leave the final word, incomplete code line, or table waiting for confirmation. A network pause alone does not close input.

Close it in the request’s cleanup path while keeping the renderer mounted. The streaming examples show this with finally.

If a code block is still waiting with a custom highlighter, also check that its promise settles. An indefinitely pending highlight can hold that code and later scheduled content.

I get an append-only error

The new string must begin with the previous source. Common causes are forwarding only the latest delta, clearing the string for a retry, editing earlier text, or reusing one instance for another answer.

Append deltas to your existing string, or pass an SDK’s accumulated snapshot directly. For replacement content, change the React/Vue key or destroy and recreate the DOM controller. This applies to static instances too.

The response restarts or suddenly finishes

Check for a changing component key, an unmount, or a change to interval, duration, mode, or reveal. Those configuration changes recreate playback in React and Vue.

In particular, avoid mode={receiving ? "streaming" : "static"}. Leave mode unchanged and close input with receiving={false}. Also avoid keys based on the changing Markdown string; use a stable response ID.

Moving a response from an active-message slot into a separate history list can remount it. Preserve its original mounted position while it is presenting.

There is no animation

Check whether the instance uses mode="static", reducedMotion="always", or the system’s reduced-motion preference. Reduced mode renders safe content immediately and remains active for that mounted response.

If you are testing normal animation after changing the preference, create a new instance. Leave the system policy enabled in application defaults.

SSR produces an empty response

With streaming mode and the default reducedMotion="system", React and Vue render a pending shell until the browser can read the motion preference. That is expected.

For already-complete content that should be visible in server HTML, mount with mode="static". See React SSR and Vue SSR.

A table or formatted passage pauses

Some source is intentionally withheld while its structure is unresolved. Tables wait for a confirmed boundary, inline formatting needs complete delimiters, and a reference link can wait for a later definition. Code highlighting may also be pending.

Increasing animation speed does not resolve these conditions. Read Markdown behavior to distinguish input stabilization from scheduled reveal.

Custom headings have gaps between letters

An override that places streamed children directly inside a flex or grid container can turn temporary reveal elements into individual layout items. A gap then separates characters or words.

Group phrasing content in an inner span while keeping sibling UI outside that span. The component override guide shows valid wrappers for this case. Do not insert spans directly beneath lists or table structures.

My styles do not apply as expected

Set a class on the Markdown root and use semantic descendant selectors. Tables live inside a scroll frame, so a selector expecting table to be a direct child of the root will not match.

When using Tailwind Typography or your own prose theme, set unstyled. It removes the default theme while preserving functional reveal CSS. Theme variables alone will not recreate the disabled theme. See Styling and CSS variables.

Avoid broad rules that change every descendant span’s display, opacity, or layout. Temporary reveal elements need those properties for presentation.

Code is plain, or the copy button is missing

Fenced code is plain by default. Install and attach the code extension for highlighting and copy controls. Inline code does not get that toolbar.

Unknown languages fall back to plain code. For custom highlighters, returned tokens must reconstruct each complete source line exactly; missing or mismatched lines fall back to plain text. Previously accepted lines keep their styles.

The copy control appears when the block is confirmed complete. A code block that is still open can already be revealing lines without offering copy yet. showLanguageLabels: false hides labels while retaining the copy control.

Screen readers announce repeated fragments

Check the surrounding conversation container for role="log", aria-live, or another live-region role. Keep the Markdown subtree outside those regions and place application status messages separately. Smoothstream already provides a completion announcement. See Accessibility.

A DOM controller update fails after navigation

update() throws after destroy(). Abort the transport and ignore late callbacks when removing the view. The DOM streaming example uses a mounted flag as well as an abort signal so pending work cannot update a destroyed controller.

Report a reproducible issue

Include the adapter and version, the Markdown source, the sequence of accumulated snapshots and receiving values, and relevant timing, motion, or styling options. For visual issues, also include the browser and whether the problem persists with default styling and no component overrides.

For streaming-specific behavior, the final Markdown alone may not reproduce the issue; the intermediate snapshots show where interpretation changed. Remove private response content before sharing an example in the GitHub repository.

© 2026 Smoothstream. All rights reserved.

Built withRadiant Docs