Decide what state belongs in a ChatGPT App component
A state-management guide for ChatGPT Apps that need to stay predictable across tool calls and UI interactions.
Open source docReal workflow example
A ChatGPT App component displays CRM leads returned by an MCP tool. Users can filter leads, expand details, select a lead, edit a draft follow-up, and ask ChatGPT to create a task. The first implementation keeps everything in React state, including selected organization, lead ownership, and whether a task has been created.
That design breaks when the tool is called again or the conversation refreshes. The component should keep temporary interaction state locally, but durable workflow state must live in the server or source system.
Implementation approach
Classify every state value before coding. Visual state includes tabs, filters, expanded rows, sort order, and selected local IDs. Draft state includes unsaved form inputs. Tool-result state is the latest structured payload from the MCP server. Durable state is anything that changes the business record.
Local state can reset without data loss. Durable state cannot. If a value decides what the user is allowed to see or whether an action already happened, it should come from the server.
When a new tool result arrives, rehydrate the component from that result and preserve only safe local preferences. This keeps repeated tool calls predictable.
Code or config snippet
type ComponentStatePlan = {
localVisual: ["activeTab", "sortKey", "expandedLeadIds"];
localDraft: ["followUpDraft"];
toolResult: ["leads", "warnings", "sourceQuery"];
serverDurable: ["leadOwner", "taskCreatedAt", "approvalStatus"];
};
function isSafeLocalState(key: string) {
return ["activeTab", "sortKey", "expandedLeadIds", "followUpDraft"].includes(key);
}
Mistakes to avoid
- Do not store authorization decisions in the component.
- Do not treat a button click as durable success before the server confirms it.
- Do not assume component state survives a repeated tool call.
- Do not hide important server errors behind optimistic UI.
- Do not keep sensitive private records in local state longer than necessary.
Ready checklist
- Every state value has an owner: local, draft, tool result, or server.
- Durable changes go through a validated tool or API call.
- Component can recover after reload or repeated tool result.
- Optimistic UI rolls back on server failure.
- Permission and ownership state comes from trusted server data.
- Partial tool results preserve layout and show clear missing-data states.
Practical tip
Add a short state ownership table to the component PR. It prevents future contributors from putting business authority into local UI state because the boundary is visible during review.
