Library / SDK
tambo-ai/tambo avatar
tambo-ai/tambo

Tambo: a generative UI SDK where your Zod schemas are the agent's tools

Generative UI SDK for React

11,222 stars576 forksTypeScriptMIT

At a glance

What is it?
Tambo turns a set of React components into the tool definitions a model can call, then streams the resulting props into your app while a backend owns the conversation. Built for teams that want the model to render their own components instead of generated markup.
Who is it for?
Tambo fits teams that already own a React component library and want a model to render those components rather than raw markup: schema registration, the provider wrapper and two hooks are the whole client surface.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • tambo-ai/tambo11,222 ★ · +41
Star history
Repository2026-09-102026-11-14
tambo-ai/tambo11.2k11.2k

41 Stars in the last 65 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Zod schemas are compiled into the tool definitions the agent calls

Tell the model which components it may use and it stops inventing markup. In Tambo that contract is a Zod schema per component, and the schema is not only validation: it becomes an LLM tool definition, so the agent calls your component the way it calls a function and the SDK renders whatever comes back. A request for sales by region lands on your chart component; adding a task lands on your task board.

Two kinds of component come out of that arrangement. A generative component renders once in response to a message, which covers charts, summaries and data visualizations. An interactable component persists across the conversation and updates as the user refines the request, which covers shopping carts, spreadsheets and task boards. A component that has to keep state over several turns therefore has to be registered the second way; a generative registration gives the user the first answer and nothing after it.

The loop is not yours to write. Tambo runs the LLM conversation and executes the agent, and what you supply is the component set plus a key for the model provider. That is the whole difference from an agent framework. LangChain and Mastra are named as frameworks Tambo works with, and the README says they are not required, but on Tambo the loop lives in the backend, which, now that Tambo Cloud has shut down (October 31, 2026), means your own Docker deployment, and the browser reaches it through the SDK.

Graph and Note: one registry, two registration shapes

Registration is a plain array of objects, one per component, each carrying a name the model sees, a description, the React component and a props schema. This is the chart case:

tsx
const components: TamboComponent[] = [
  {
    name: "Graph",
    description: "Displays data as charts using Recharts library",
    component: Graph,
    propsSchema: z.object({
      data: z.array(z.object({ name: z.string(), value: z.number() })),
      type: z.enum(["line", "bar", "pie"]),
    }),
  },
];

The enum does real work. Because type is z.enum(["line", "bar", "pie"]) rather than a free string, the model is held to three values instead of inventing a chart style, and every value it sends is one the component already handles.

The interactable case wraps an existing component instead of listing it in the array:

tsx
const InteractableNote = withInteractable(Note, {
  componentName: "Note",
  description: "A note supporting title, content, and color modifications",
  propsSchema: z.object({
    title: z.string(),
    content: z.string(),
    color: z.enum(["white", "yellow", "blue", "green"]).optional(),
  }),
});

Note the optional() on color. A prop the model may leave out has to be declared optional, or a generated call fails validation over a field the user never asked about. The two shapes take their configuration from the same place, so moving a component from one to the other is a change to how it is declared.

TamboProvider needs a userKey or a userToken before a thread exists

The app is wrapped once in TamboProvider, which carries the API key, the registered components and the identity of the thread owner:

tsx
<TamboProvider
  apiKey={process.env.NEXT_PUBLIC_TAMBO_API_KEY!}
  userKey={currentUserId}
  components={components}
>
  <Chat />
  <InteractableNote id="note-1" title="My Note" content="Start writing..." />
</TamboProvider>

One of userKey or userToken has to be there, because that value identifies who owns the thread and there is nothing to attach a conversation to without it. The choice between them is about where the identity comes from. userKey suits server-side and trusted environments, where your own code already knows the user id. userToken takes an OAuth access token that carries the user identity, the shape a client-side app has when the identity lives in the token rather than in your server.

The provider key comes from NEXT_PUBLIC_TAMBO_API_KEY in the examples, and the variable name marks it as public. The README says to bring your own API key for OpenAI, Anthropic, Gemini, Mistral or any OpenAI-compatible provider.

useTambo and useTamboThreadInput split reading from submitting

Two hooks cover the surface most apps touch:

tsx
const { messages, isStreaming } = useTambo();
const { value, setValue, submit, isPending } = useTamboThreadInput();

useTambo() is the read side: messages, streaming state and thread management. useTamboThreadInput() is the write side: the current input value, a setter, submit and isPending. The split matters for a UI because isStreaming tells the component tree that props are still arriving while isPending tells the input box that a submission is in flight, and those are not the same state. Props stream to your components as the model generates them, so a single answer renders more than once, and any registered component holding its own state has to survive those re-renders.

npm create tambo-app initializes git and runs a cloud setup script

The quickstart is three commands:

bash
npm create tambo-app my-tambo-app  # auto-initializes git + tambo setup
cd my-tambo-app
npm run dev

The comment on the first line deserves a read before it runs. This is not only a template generator: it initializes a git repository and runs a tambo setup step.

Tambo Cloud was the hosted backend that managed conversation state and agent orchestration; new signups are closed and it shut down on October 31, 2026. Self-hosting runs the same backend on your own infrastructure through Docker. Beyond the generated project there is a pre-built component library of agent and generative UI primitives at ui.tambo.co, and two templates to fork: a chat interface with dynamic component generation, and an analytics dashboard with AI-powered visualization.

Self-hosting publishes Postgres on 5433 and MinIO on 9000 with default credentials

The self-hosted path is the same backend under Docker, and docker-compose.yml at the repository root shows the wiring. Services in the file include MinIO as S3-compatible object storage, PostgreSQL 17, and the web service built from apps/web/Dockerfile. Postgres is moved off its default port and given an explicit data directory:

yaml
    environment:
      POSTGRES_DB: ${POSTGRES_DB:-tambo}
      POSTGRES_USER: ${POSTGRES_USER:-postgres}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
      PGDATA: /var/lib/postgresql/data/pgdata
    volumes:
      - tambo_postgres_data:/var/lib/postgresql/data
    ports:
      - "5433:5432"

The defaults in that file are worth checking before this runs anywhere but a laptop. The Postgres password falls back to postgres unless docker.env overrides it, and the MinIO service sets MINIO_ROOT_USER and MINIO_ROOT_PASSWORD to minioadmin outright. A deployment that copies docker-compose.yml without writing a docker.env starts with credentials that are printed in the repository.

The port choices collide with common local setups as well: 5433 is a frequent local Postgres, and MinIO takes 9000 and 9001. The web service publishes 8260 mapped to 3000, and NEXT_PUBLIC_TAMBO_API_URL defaults to http://localhost:8261. SELF-HOSTING.md sits at the repository root beside CONTRIBUTING.md and RELEASING.md for the longer version of this.

MCP servers are configured as data, with MCPTransport picked per entry

Tambo speaks the full MCP protocol, and the README names tools, prompts and elicitations.

The root package is @tambo-ai/repo, and releases are cut per package

The repository root is a monorepo whose own metadata does not describe what an application installs. Its package.json is named @tambo-ai/repo at version 1.0.0, and the description field still carries the badges of another name, hydra-ai, pointing at github.com/michaelmagan/hydraai. The SDK itself lives in react-sdk/ and is published as @tambo-ai/react. The build commands are turbo monorepo tasks: build runs turbo build, and build:sdk narrows it to turbo build --filter=@tambo-ai/react. A reader who copies 1.0.0 into a dependency list is pinning a workspace version, not a release of the SDK.

Releases are cut per package rather than for the repository as a whole. The latest releases listed are web v0.136.0 (2026-10-09), react v1.4.0 (2026-10-08) and client v1.3.0 (2026-10-08), on three separate version lines, and the repository has no single tag that covers the SDK. The last push to the repository was on 2026-11-01. Which version line to pin is a decision the release list leaves to each consumer.

Editorial conclusion

Tambo fits teams that already own a React component library and want a model to render those components rather than raw markup: schema registration, the provider wrapper and two hooks are the whole client surface. It does not fit teams who need the agent loop under their own control, and it does not fit a static site with no backend, since conversation state and agent execution live in the backend which, since Tambo Cloud shut down on October 31, 2026, means your own Docker deployment. Before adopting it, run npm create tambo-app, set NEXT_PUBLIC_TAMBO_API_KEY, and check the two defaults that ship in docker-compose.yml, where POSTGRES_PASSWORD falls back to postgres and MinIO starts as minioadmin. Pin the package you consume, @tambo-ai/react, not the root workspace version 1.0.0.

Frequently asked questions

Which LLM providers does Tambo work with?

You bring your own API key. The list given is OpenAI, Anthropic, Gemini, Mistral, or any OpenAI-compatible provider.

Do I have to use LangChain or Mastra with Tambo?

No. Tambo runs the LLM conversation loop for you, and LangChain and Mastra are named as agent frameworks it works with rather than requirements.

Can I run Tambo without Tambo Cloud?

Yes. Self-hosting runs the same backend on your own infrastructure via Docker, and docker-compose.yml at the repository root wires PostgreSQL, MinIO and the web service.

Which npm package holds the Tambo React SDK?

It is published as @tambo-ai/react. The MCP example imports MCPTransport from @tambo-ai/react/mcp, and the repository's build:sdk script filters for that package name.

How does Tambo decide which component to render?

You register components with Zod schemas, those schemas become LLM tool definitions, and the agent calls them like functions. Tambo renders the result and streams the props.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. tambo-ai/tambo on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tambo-ai-tambo.svg)](https://hysenlabs.com/projects/tambo-ai-tambo)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/tambo-ai-tambo.svg)](https://hysenlabs.com/projects/tambo-ai-tambo)

Was this useful?