---
title: Troubleshooting
description: Diagnose missing text, replayed animation, lifecycle errors, and styling or highlighting surprises.
---

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](/get-started/streaming) 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](/adapters/react#nextjs-and-ssr) and [Vue SSR](/adapters/vue#ssr-and-nuxt).

## 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](/guides/markdown) 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](/customize/components#group-children-in-flex-and-grid-layouts) 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](/customize/styling) and [CSS variables](/reference/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](/customize/syntax-highlighting) 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](/guides/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](/get-started/streaming#connect-a-stream) 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](https://github.com/stefan-joseph/smoothstream).
