# Syncing editable interactables URL: /concepts/components/syncing-editable-interactables When you want a user-editable component to stay aligned with the props that Tambo controls, you only need a thin wrapper: keep the edited props in state, pass them straight into the interactable, and adopt any remote changes with a `useEffect`. ## Why props fall out of sync * The component stores user edits in local state, so Tambo never sees the new props. * Tambo updates the registry, but the component keeps rendering the stale value. Fix both directions by treating the wrapper state as the single source of truth. ## Step by step ### 1. Presentational component ```tsx type NoteProps = { title: string; content: string; color?: "white" | "yellow" | "blue" | "green"; }; function Note({ title, content, color = "yellow" }: NoteProps) { return (

{title}

{content}

); } ``` * No state, just props. Tambo controls whatever you pass in. ### 2. Register it with `withInteractable` ```tsx import { withInteractable } from "@tambo-ai/react"; import { z } from "zod"; const InteractableNote = withInteractable(Note, { componentName: "Note", description: "A note that both users and Tambo can edit", propsSchema: z.object({ title: z.string(), content: z.string(), color: z.enum(["white", "yellow", "blue", "green"]).optional(), }), }); ``` * The schema tells Tambo exactly which props it can update. ### 3. Wrap it with local state + a sync effect ```tsx import { useEffect, useState } from "react"; import { useCurrentInteractablesSnapshot } from "@tambo-ai/react"; function EditableNote(initial: NoteProps) { const [note, setNote] = useState(initial); const [interactableId, setInteractableId] = useState(null); const snapshot = useCurrentInteractablesSnapshot(); // Pull in any updates that Tambo makes to this interactable. useEffect(() => { if (!interactableId) return; const match = snapshot.find((item) => item.id === interactableId); if (!match) return; const next = match.props as Partial; setNote((prev) => ({ ...prev, ...next })); }, [snapshot, interactableId]); return (