Shared State (Tandem)
What is Shared State?
Section titled “What is Shared State?”While AI Actions let the agent perform tasks, Shared State gives the agent eyes and hands inside your application. It creates a two-way sync between your app’s UI state (like a form or settings panel) and the agent. The agent reads the current state of your UI in real time and writes changes back to it.
Why use Shared State?
Section titled “Why use Shared State?”- Contextual awareness — the agent sees exactly what the user sees, so it can give relevant help without asking clarifying questions.
- Interactive guidance — instead of just describing steps, the agent can highlight fields, pre-fill values, and walk users through complex workflows.
- Seamless task automation — the agent can update your UI directly, turning multi-step processes into a single conversation.
How it works: a two-way street
Section titled “How it works: a two-way street”- Your App → Agent: your app calls
shareState()whenever the UI changes, keeping the agent’s picture of the current screen up to date. - Agent → Your App: when the agent decides to act, it pushes updates through the handler you registered, and your UI re-renders instantly.
flowchart LR
subgraph Your App
UI[UI Component]
SF["shareState(key, state)"]
end
subgraph Foldspace
AG[Agent]
end
UI -- "User types ➜ setForm()" --> SF
SF -- "State snapshot" --> AG
AG -- "Agent updates ➜ handler(key, next)" --> UI
Methods
Section titled “Methods”Two methods on the agent instance manage the whole process.
shareState(key, state, handler, stateDescription)
Section titled “shareState(key, state, handler, stateDescription)”Establish and update synchronized state. Call it once on initialization, then again every time the user-side state changes.
| Parameter | Type | Required | Description |
|---|---|---|---|
key | string | Yes | A unique string identifier for this piece of state (e.g. "contact_form"). |
state | object | Yes | A snapshot of the current UI state object (e.g. { name: "", email: "" }). |
handler | (key: string, nextState: any) => void | No | Optional, but required to let the agent push updates back to your UI. Pass it for two-way sync. |
stateDescription | string | No | A free-form string that adds context about the state. Can describe the type/model, usage instructions, or provide a human-readable explanation. |
persistAcrossPages | boolean | No |
// Initial sync: Register the state key, initial data, and the handlerconst agentKey = "YOUR_AGENT_KEY";const agent = window.foldspace?.agent(agentKey);agent.current?.shareState(stateKey, form, handleStateChange, stateDescription);Guide the agent with stateDescription
Section titled “Guide the agent with stateDescription”stateDescription is optional but gives context to both developers and the agent.
Though concise by design, it can carry different kinds of guidance:
- Naming conventions: e.g.
"All keys in this state should be snake_case". - Model structure hints: e.g.
"If 'items' is an empty array, each entry should contain { id: string, quantity: number }". - Purpose description: e.g.
"Tracks the shipping address details required for checkout".
stateDescription enriches the state with descriptive or instructional metadata, so
the UI and the agent interpret the state consistently.
clearState(key)
Section titled “clearState(key)”Tell the agent to stop tracking a piece of state. Call it for cleanup when a component unmounts or the user navigates away, preventing memory leaks.
| Parameter | Type | Required | Description |
|---|---|---|---|
key | string | Yes | The unique string identifier for the state you wish to clear. |
Sync a React form
Section titled “Sync a React form”Synchronize a React form component’s state with the agent using hooks.
import { useEffect, useRef, useState } from "react";
type FormState = { name: string; email: string; company?: string;};
interface Props { agentKey: string;}
export default function SharedStateForm({ agentKey }: Props) { // 1. Local UI state is kept minimal. const [form, setForm] = useState<FormState>({ name: "", email: "", company: "" });
// 2. The agent instance is created once and stored in a ref. const agentRef = useRef<any | null>(null); const stateKey = "contact_form"; const stateDescription = ` type FormState = { name: string; email: string; company?: string; };
interface Props { agentKey: string; }`;
// 3. `useEffect` handles the entire lifecycle: setup, subscription, and cleanup. useEffect(() => { // Initialize the agent agentRef.current = window.foldspace?.agent(agentKey);
// Agent -> UI: Define the handler to receive updates FROM the agent const handleStateChange = (key: string, nextState: unknown) => { if (key === stateKey) { setForm(nextState as FormState); // Update local state } };
// Initial sync: Register the state key, initial data, and the handler agentRef.current?.shareState(stateKey, form, handleStateChange, stateDescription);
// Cleanup function: Tell the agent to stop tracking on unmount return () => { agentRef.current?.clearState(stateKey); agentRef.current = null; }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [agentKey]);
// 4. UI -> Agent: Call this on user input to keep the agent's snapshot fresh. const updateField = <K extends keyof FormState>(key: K, value: FormState[K]) => { const next = { ...form, [key]: value }; setForm(next); // Send the fresh snapshot to the agent agentRef.current?.shareState(stateKey, next); };
// 5. Render your form inputs and call updateField on change. return <>...</>;}sequenceDiagram
participant App as Your App
participant SDK as Foldspace SDK
participant Agent as Agent
App->>SDK: shareState("form", data, handler)
SDK->>Agent: State snapshot
Note right of Agent: Agent reads form context
Agent-->>SDK: Updated state
SDK-->>App: handler("form", nextState)
App->>App: setForm(nextState)
Related
Section titled “Related”- AI Actions: give your agent custom actions that automate workflows and return context-aware responses.
- Knowledge Base: connect a knowledge base for accurate, context-rich answers.