Tambo Lockup

Streaming Props into State

Stream component props into components with a few lines of code and no need for useEffect dependency hell.

When working with AI-generated content in Tambo, you often need to update your component state when new content streams in. The useTamboStreamingProps hook simplifies this common pattern.

Building on Previous Concepts

This guide combines two key Tambo concepts:

  1. Streaming - The ability to receive AI-generated content in real-time as it's being created
  2. Component State - Tambo's state management for tracking component state as context for AI

We recommend you read those first.

When using streaming, props arrive incrementally, and you need an efficient way to update your component state without complex useEffect dependencies. useTamboStreamingProps solves this exact problem.

Why this matters

React was designed around discrete renders, but large language models deliver continuous streams of tokens.
Trying to glue the two paradigms together with ad-hoc useEffect logic quickly leads to:

  • Duplicate diffing code across components
  • Oversized dependency arrays that are hard to reason about
  • Extra re-renders that waste CPU and battery

useTamboStreamingProps centralises the diffing and batching so your component only re-renders when something actually changed.

Real-world numbers: while streaming at ~4 tokens / sec we measured ~80 % fewer renders and ~65 % less main-thread time compared with a naive useEffect that writes state on every chunk.

DIY foot-guns

  • Stale closures – forgetting to include the latest setter in the dependency array
  • Infinite loops – updating state unconditionally inside an effect that depends on that same state
  • Out-of-order writes – multiple async effects racing to write conflicting values
  • Performance hits from excessive renders – diffing large objects on every chunk
  • Missed updates – early-return logic that skips valid but falsy values

Using useTamboStreamingProps

import {
  useTamboComponentState,
  useTamboStreamingProps,
} from "@tambo-ai/react";

function MyComponent({ streamedTitle, streamedBody }) {
  // 1. Set up your Tambo component state
  const [state, setState] = useTamboComponentState("myState", {
    title: "",
    body: "",
  });

  // 2. Connect streaming props to your state
  useTamboStreamingProps(state, setState, {
    title: streamedTitle,
    body: streamedBody,
  });

  // That's it! Your state will automatically update when props change
  return (
    <div>
      <h1>{state?.title}</h1>
      <p>{state?.body}</p>
    </div>
  );
}

Benefits

  • Eliminates repetitive useEffect code
  • Automatically detects and applies changes
  • Only updates when values actually change
  • Works with any state shape
  • Type-safe with TypeScript

Without vs With

Before (repetitive pattern):

useEffect(() => {
  if (state) {
    const shouldUpdateTitle = streamedTitle && streamedTitle !== state.title;
    const shouldUpdateBody = streamedBody && streamedBody !== state.body;

    if (shouldUpdateTitle || shouldUpdateBody) {
      setState({
        ...state,
        title: shouldUpdateTitle ? streamedTitle : state.title,
        body: shouldUpdateBody ? streamedBody : state.body,
      });
    }
  }
}, [streamedTitle, streamedBody]);

After (clean and simple):

useTamboStreamingProps(state, setState, {
  title: streamedTitle,
  body: streamedBody,
});

Don't want to use Tambo's state management? You can use your own!

const [state, setState] = useState({
  title: "",
  body: "",
});

useTamboStreamingProps(state, setState, {
  title: streamedTitle,
  body: streamedBody,
});

Works the same, but you don't get the benefits of Tambo's state management.

When not to use useTamboStreamingProps

  • The prop arrives once (e.g. static config fetched at build time).
  • You need custom reconciliation that can't be expressed as a shallow merge.
  • The incoming object is huge and diffing would dominate render time.
  • You already debounce/queue updates up-stream (e.g. via a websocket buffer).
  • You're on React < 18 and can't rely on concurrent rendering semantics.

Glossary

  • dependency array - the second argument to useEffect that tells React when the effect should re-run.
  • useEffect - a React hook for performing side-effects after a component render. See the official docs for details.