Stream React trees recursively, LLM-style progressive rendering.
npm install react-tree-stream
A flexible React component for creating "streaming" or "typewriter" effects on your content. It intelligently renders text, components, and even nested streams sequentially.
- Text Streaming: Renders text content word-by-word, like a typewriter.
- Component Rendering: Instantly renders any non-text React components.
- Nested Streams: Supports nesting TreeStream components, waiting for each to complete before continuing.
- Customizable: Control the streaming speed and the underlying HTML element.
- Callbacks: onComplete event fires when the entire stream is finished.
- Dynamic Content: Automatically restarts the stream if its children change.
- Styling Hooks: Provides data-streaming and data-complete attributes for easy CSS styling.
- Type-Safe: Fully typed with TypeScript, including props for the underlying element.
``bash`
npm install react-tree-streamor
yarn add react-tree-stream
Wrap your content with TreeStream to start streaming.
`tsx
import { TreeStream } from 'react-tree-stream';
function App() {
return (
This is a simple example of streaming text content. The component will render this sentence
word by word.
);
}
`
TreeStream can handle a mix of text and React components. Text is streamed, while components are rendered instantly.
`tsx
import { TreeStream } from 'react-tree-stream';
const MyComponent = () => (
function App() {
return (
Here is some text.
And here is some more text that will appear after the component.
);
}
`
You can nest TreeStream components. The parent stream will pause and wait for the nested stream to complete before it continues.
`tsx
import { TreeStream } from 'react-tree-stream';
function App() {
return (
This is the parent stream. It will pause here...
...and this nested stream will run to completion. Once it's done...
...the parent stream will resume.
);
}
`
By default, TreeStream streams text content word-by-word. You can change this behavior to stream text character-by-character.
`tsx
import { TreeStream } from 'react-tree-stream';
function App() {
return (
This text is being streamed one character at a time. You can change the streaming behavior
to be more granular.
);
}
`
propTreeStream is polymorphic: you can render it as any element or component using as?: React.ElementType.
- Semantic tags: data-*
- Custom components: pass your wrapper and forward props to a DOM element so , className, and style reach the node.
- Fragments: renders no wrapper. In this mode, DOM-specific props like className and style are disallowed and the data-* attributes are not rendered (no wrapper node).
Example custom wrapper:
`tsx
const Custom = ({ children, ...rest }: React.PropsWithChildren
{children}
);
`
When using as={React.Fragment}, omit DOM-only props:
`tsx`
The component accepts the following props:
| Prop | Type | Default | Description |
| ------------ | ---------------------------------- | ------- | ------------------------------------------------------------------------------------------------------- |
| as | React.ElementType | 'div' | The element or component to render as the root; supports intrinsic tags, custom components, or React.Fragment. |children
| | React.ReactNode | | The content to be streamed. |speed
| | number | 5 | The number of units (words, characters, or components) to render per tick. |interval
| | number | 50 | The delay in milliseconds between each rendering tick. |autoStart
| | boolean | true | If true, the stream starts automatically on mount. If false, it waits for autoStart to become true. |onComplete
| | () => void | | A callback function that is invoked when the entire stream has finished rendering. |streamBy
| | 'word' \| 'character' | 'word'| Determines the granularity of the streaming. Use 'word' for word-by-word streaming or 'character' for character-by-character streaming. |...rest
| | ComponentPropsWithoutRef | | Any other props are forwarded to the chosen element/component. For React.Fragment, DOM-only props are not allowed. |
The root element rendered by TreeStream includes data attributes that reflect its current state, which you can use for styling.
- data-tree-stream: Always present on the component's root element.data-streaming="true"
- : Present while the component is actively streaming text or waiting for a nested stream.data-complete="true"
- : Present when the stream has finished.
You can use these attributes to create a blinking cursor effect that appears only during streaming.
`css
/ Your CSS file /
[data-streaming='true']::after {
content: '▋';
animation: blink 1s step-end infinite;
}
@keyframes blink {
50% {
opacity: 0;
}
}
``