Interactable Components
Allow Tambo to update your pre-placed components
When you want to place specific components on screen rather than letting Tambo choose which to show, but still want to allow your users to interact with them using natural language, use Tambo's Interactable components.
Unlike regular registered components that Tambo generates and renders from scratch when responding to messages, interactable components are pre-placed by you while still allowing Tambo to modify their props.
Creating Interactable Components
The easiest way to make a component interactable to Tambo is by using withInteractable
. Pass in your component, a name, a description, and the props schema, and get an interactable version of your component that Tambo knows about.
import { withInteractable } from "@tambo-ai/react";
import { z } from "zod";
// Your existing component
function Note({ title, content, color = "white" }) {
return (
<div className={`note note-${color}`}>
<h3>{title}</h3>
<p>{content}</p>
</div>
);
}
// Make it interactable
const InteractableNote = withInteractable(Note, {
componentName: "Note",
description:
"A simple note component that can display title and content with different colors",
propsSchema: z.object({
title: z.string(),
content: z.string(),
color: z.enum(["white", "yellow", "blue", "green"]).optional(),
}),
});
// Another simple component
function Counter({ count, label }) {
return (
<div className="counter">
<span>
{label}: {count}
</span>
</div>
);
}
const InteractableCounter = withInteractable(Counter, {
componentName: "Counter",
description: "A simple counter that displays a label and count value",
propsSchema: z.object({
count: z.number(),
label: z.string(),
}),
});
// Place both components in your app
function Page() {
return (
<div>
<InteractableNote
title="Welcome"
content="This is a simple note that Tambo can update!"
color="yellow"
/>
<InteractableCounter count={42} label="Items" />
</div>
);
}
Now Tambo is able to read and update these components in-place when responding to messages. Users can ask things like:
- "Change the note title to 'Important Reminder'"
- "Update the note content to 'Don't forget the meeting at 3pm'"
- "Make the note blue"
- "Set the counter to 100"
- "Change the counter label to 'Tasks Completed'"
InteractableConfig
When using withInteractable
, you provide a configuration object describing the component to Tambo:
interface InteractableConfig {
componentName: string; // Name for Tambo to reference
description: string; // Description of what the component does
propsSchema: z.ZodTypeAny; // Schema of props for Tambo to generate
}
How it Works
For each component marked as interactable using withInteractable
, behind the scenes Tambo stores a state object representing the props of the component, and registers a tool to update that object.
When Tambo decides to update an interactable component while responding to a message, it uses that component's 'update' tool, which updates the state and triggers a re-render of your wrapped component.
Integration with Tambo Provider
Make sure your app is wrapped with the <TamboProvider/>
.
import { TamboProvider } from "@tambo-ai/react";
function App() {
return (
<TamboProvider>
{/* Your app with interactable components */}
<InteractableNote />
<InteractableCounter />
</TamboProvider>
);
}
This creates a truly conversational interface where users can modify your UI through natural language, making your applications more accessible and user-friendly.
Automatic Context Awareness
When you use TamboInteractableProvider
, your interactable components are automatically included in the AI's context. This means:
- The AI knows what components are currently on the page
- Users can ask "What's on this page?" and get a comprehensive answer
- The AI can see the current state (props) of all interactable components
- Component changes are reflected in real-time
No additional setup required - this context is provided automatically and can be customized or disabled if needed.
Example Interactions
With interactable components on the page, users can ask:
- "What components are available?"
- "Change the note title to 'Important Reminder'"
- "Show me the current state of all my components"
- "Make the counter red and set it to 100"
Customizing Automatic Context
The automatic context can be disabled, enabled selectively, or customized to show only specific information.
Disable Globally
To disable interactables context across your entire app:
<TamboProvider
apiKey={apiKey}
contextHelpers={{
// Disable interactables context globally
interactables: () => null,
}}
>
<TamboInteractableProvider>
{/* Interactables context is disabled, but components still work */}
<InteractableNote title="Hidden from AI" />
</TamboInteractableProvider>
</TamboProvider>
Enable Locally (Override Global Disable)
If you've disabled it globally but want to enable it for a specific page or section:
function SpecificPage() {
const { addContextHelper } = useTamboContextHelpers();
const snapshot = useCurrentInteractablesSnapshot();
React.useEffect(() => {
// Re-enable interactables context for this page only
const helper = () => {
if (snapshot.length === 0) return null;
return {
description: "Interactable components on this page that you can modify",
components: snapshot.map((component) => ({
id: component.id,
componentName: component.name,
description: component.description,
props: component.props,
propsSchema: component.propsSchema ? "Available" : "Not specified",
})),
};
};
addContextHelper("interactables", helper);
}, [addContextHelper, snapshot]);
return (
<TamboInteractableProvider>
{/* Context is now enabled for this page */}
<InteractableNote title="Visible to AI" />
</TamboInteractableProvider>
);
}
Custom Context (IDs Only Example)
Sometimes you might want to share summary information and have the AI request the full context when needed.
This is an example of how to only IDs and names with every message:
import {
useCurrentInteractablesSnapshot,
useTamboContextHelpers,
} from "@tambo-ai/react";
function IdsOnlyInteractables() {
const { addContextHelper } = useTamboContextHelpers();
const snapshot = useCurrentInteractablesSnapshot();
React.useEffect(() => {
const idsOnlyHelper = () => {
if (snapshot.length === 0) return null;
return {
description: "Available interactable component ids.",
components: snapshot.map((component) => ({
id: component.id,
componentName: component.name,
// Deliberately omit props.
})),
};
};
// Override the default helper with our ids only version
addContextHelper("interactables", idsOnlyHelper);
}, [addContextHelper, snapshot]);
return null; // This component just sets up the context helper
}
// Usage
<TamboInteractableProvider>
<PrivacyFriendlyInteractables />
<InteractableNote title="Not visible unless requested." />
<InteractableCounter count={42} />
</TamboInteractableProvider>;
Filter by Component Type
Maybe you only want to show certain types of components.
Here is an example of how you could filter by component type:
function FilteredInteractablesContext() {
const { addContextHelper } = useTamboContextHelpers();
const snapshot = useCurrentInteractablesSnapshot();
React.useEffect(() => {
const filteredHelper = () => {
// Only show Notes and Counters, hide other component types
const allowedTypes = ["Note", "Counter"];
const filteredComponents = snapshot.filter((component) =>
allowedTypes.includes(component.name),
);
if (filteredComponents.length === 0) return null;
return {
description: "Available interactable components (filtered)",
components: filteredComponents.map((component) => ({
id: component.id,
componentName: component.name,
props: component.props,
})),
};
};
addContextHelper("interactables", filteredHelper);
}, [addContextHelper, snapshot]);
return null;
}