---
title: React
description: Render streaming Markdown with the Smoothstream React component, including Next.js, static history, and class names.
---

`@smoothstream/react` is the React adapter. It drives the shared engine from animation frames and turns presentation snapshots into React elements.

<CodeGroup>
```bash title="npm" hideLanguageIcon
npm install @smoothstream/react
```

```bash title="pnpm" hideLanguageIcon
pnpm add @smoothstream/react
```

```bash title="yarn" hideLanguageIcon
yarn add @smoothstream/react
```

```bash title="bun" hideLanguageIcon
bun add @smoothstream/react
```
</CodeGroup>

Peer dependencies: `react` and `react-dom` at 18.2 or 19. Reveal CSS and the default prose theme import automatically.

## Render Markdown children

Pass the accumulated Markdown string as `children`. Other child types throw.

```tsx title="AssistantMessage.tsx"
import { Smoothstream } from "@smoothstream/react";

export function AssistantMessage({
  text,
  receiving,
}: {
  text: string;
  receiving: boolean;
}) {
  return (
    <Smoothstream className="assistant-markdown" receiving={receiving}>
      {text}
    </Smoothstream>
  );
}
```

`className` is applied to the Markdown root. Use it for layout and [token overrides](/customize/styling).

<Callout type="tip" title="Follow the streaming contract">
  The usage rules in [Streaming a response](/get-started/streaming) apply here: accumulated snapshots, `receiving` while the model is still writing, and a new `key` for a different answer.
</Callout>

## Chat messages

Keep the in-flight answer in streaming mode. Render stored messages as static so they appear immediately and skip the completion announcement.

```tsx
<>
  {history.map((message) => (
    <Smoothstream key={message.id} mode="static">
      {message.content}
    </Smoothstream>
  ))}
  <Smoothstream key={active.id} receiving={active.receiving}>
    {active.content}
  </Smoothstream>
</>
```

When `receiving` becomes `false`, leave that streaming instance mounted until presentation finishes. Start the next assistant turn with a new `key`.

## Next.js and SSR

The published entry is a Client Component, so you can import `Smoothstream` from a Server Component and still get a client boundary.

`mode="static"` emits settled semantic HTML on the server and hydrates in place.

In streaming mode with `reducedMotion="system"` (the default), the server and first hydration share an empty motion-pending shell. The browser then reads `prefers-reduced-motion` before Markdown appears. An empty stream can resolve that policy while it waits for the first token. Forced `"always"` and `"never"` policies are deterministic during SSR and skip the pending state.

Browser-only work such as image loading, syntax highlighting, and clipboard copy starts after mount.

## Timing props remount playback

Changing `interval`, `duration`, `mode`, or `reveal` on a mounted component remounts playback so the new timing applies to a fresh schedule. Treat those as per-response settings, not knobs to twist while tokens arrive.

Full option list: [API](/reference/api).
