React SDK
The React SDK lets you embed ggui-generated UIs directly inside your React application. <McpAppIframe> is the canonical surface for hosts; the legacy <GguiSession>-family primitives (retained for devtool only) give in-process stack visibility for debugging.
Installation
Section titled “Installation”npm install @ggui-ai/reactQuick Example — <McpAppIframe>
Section titled “Quick Example — <McpAppIframe>”import { McpAppIframe, type ProtocolError } from "@ggui-ai/react";import { useEffect, useState } from "react";
type ResourceContents = { uri: string; mimeType: string; text: string };
function App({ sessionId }: { sessionId: string }) { const [resource, setResource] = useState<ResourceContents | null>(null);
useEffect(() => { // Fetch the MCP Apps `{contents:[{uri,mimeType,text}]}` envelope from your // session-resource endpoint. // 🟢 OSS: GET http://127.0.0.1:6781/s/<shortCode>/resource // 🟣 Hosted Guuey: GET /ggui/session-resource?session=<id> (Cognito Bearer) fetch(`/ggui/session-resource?session=${sessionId}`, { headers: { Authorization: `Bearer ${idToken}` }, }) .then((r) => r.json()) .then((body) => setResource(body.contents[0])); }, [sessionId]);
if (!resource) return <p>Loading…</p>;
return <McpAppIframe resource={resource} onError={(err: ProtocolError) => console.error(err)} />;}The iframe boots the @ggui-ai/renderer bundle off the URL advertised in the resource’s _meta.ggui.bootstrap.rendererUrl, opens the channel-3 WebSocket back to the server, and renders stack items. Your host code stops there. For the complete protocol contract, see Implementing the ggui Protocol.
Legacy primitives (Phase 3 retirement targets)
Section titled “Legacy primitives (Phase 3 retirement targets)”Everything below this line documents the pre-<McpAppIframe> primitives. They still work and still ship; the @ggui-ai/devtool routes still import them for in-process stack visibility during debugging. The internal shells (<ChatShell> / <AgentShell> / <FullscreenShell>) were already migrated off these primitives in Phase 3 Wave 2 — they now wrap <McpAppIframe> internally. New consumer code should not import from this set — use <McpAppIframe> above.
Legacy quick example
Section titled “Legacy quick example”import { GguiProvider, GguiSession, StackItemRenderer } from "@ggui-ai/react";
function App() { return ( <GguiProvider appId="app_..." wsEndpoint="wss://ws.guuey.com"> <GguiSession sessionId="session_..."> {({ stack, action, connectionStatus }) => ( <div> {stack.map((item) => ( <StackItemRenderer key={item.id} stackItem={item} onAction={action} /> ))} <p>Connection: {connectionStatus}</p> </div> )} </GguiSession> </GguiProvider> );}🟢 Running OSS / self-hosted? Point wsEndpoint at your local ggui serve WebSocket (default ws://127.0.0.1:6781/ws). The legacy primitives are protocol-agnostic.
Components
Section titled “Components”GguiProvider
Section titled “GguiProvider”Root provider that supplies app configuration to all ggui components via React Context.
<GguiProvider appId="app_..." wsEndpoint="wss://ws.guuey.com"> {children}</GguiProvider>| Prop | Type | Required | Description |
|---|---|---|---|
appId | string | Yes | Your ggui app ID |
wsEndpoint | string | No | WebSocket endpoint URL. If omitted, real-time features are disabled |
adapters | ('voice' | 'camera')[] | No | Device adapters to initialize |
GguiSession
Section titled “GguiSession”Manages a session’s lifecycle, WebSocket connection, and canonical {@link ActionEnvelope} emission. Must be inside a <GguiProvider>.
<GguiSession sessionId={sessionId} onInteraction={(envelope) => console.log(envelope.type, envelope.payload)} onError={(error) => console.error(error)}> {({ action, connectionStatus }) => ( <div> {connectionStatus !== "connected" && <p>Reconnecting...</p>} <button onClick={() => action({ rating: 5 })}>Submit</button> </div> )}</GguiSession>Key props:
| Prop | Type | Description |
|---|---|---|
sessionId | string | Session ID to connect to |
userToken | string | User authentication token |
onInteraction | (envelope: ActionEnvelope) => void | Called for interaction:* event types. Receives the canonical flat envelope |
onBeforeAction | <T>(data: T, meta: ActionMeta) => T | undefined | Middleware before action emission. Return undefined to cancel; return data to proceed |
onStackPush | (stackItem: StackItem) => void | Called when a card is pushed |
onError | (error: Error) => void | Error handler. Receives ClientContractViolationError for contract violations |
ActionMeta is the minimal context passed to onBeforeAction — just {sessionId, stackIndex}. The legacy diagnostic bag (device info, interface context) was retired when the emitter migrated to canonical ActionEnvelope; those values live on the session, not per-delivery.
Render prop API (SessionApi):
| Field | Type | Description |
|---|---|---|
action | <T>(data: T) => void | Submit user interaction data. Emits a canonical data:submit ActionEnvelope |
send | (msg: WebSocketMessage) => void | Send a raw WebSocket message. Internal SDK use (feedback, diagnostics) |
connectionStatus | ConnectionStatus | 'connecting' | 'connected' | 'disconnected' | 'reconnecting' |
stack | StackItem[] | Current session stack |
sessionId | string | Session ID |
User text messages (channel 1) flow through useInvoke() at the consumer layer — not through SessionApi. GguiSession exposes stack + action + raw-send via its render prop.
DynamicComponent
Section titled “DynamicComponent”Renders compiled JavaScript components at runtime. Takes compiled ESM code and dynamically imports it.
<DynamicComponent code={compiledJsCode} props={{ title: "Hello" }} />| Prop | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Compiled JavaScript code (ESM module with default export) |
props | Record<string, unknown> | No | Props to pass to the rendered component |
fallback | ReactNode | No | UI shown while loading |
onError | (error: Error) => void | No | Error handler |
StackItemRenderer
Section titled “StackItemRenderer”Handles the controller-template pattern automatically. If a stack item has a controller, it wraps the template; otherwise renders the template directly.
<StackItemRenderer stackItem={item} onAction={action} onError={console.error} />SelfRepairBoundary
Section titled “SelfRepairBoundary”Error boundary that catches runtime errors and sends them to the server for automatic repair. Premium feature.
<SelfRepairBoundary sessionId="session_123" appId="app_456" stackItemId="stack_789" config={{ enabled: true, maxAttempts: 3, retryDelayMs: 1000, showRepairUI: true }} onReportError={reportError}> <DynamicComponent code={compiledCode} /></SelfRepairBoundary>useGguiContext
Section titled “useGguiContext”Access the GguiProvider context. Must be used within a <GguiProvider>.
const { appId, wsEndpoint, adapters } = useGguiContext();useWebSocket
Section titled “useWebSocket”Manage a WebSocket connection with automatic reconnection. Returns sendAction for emitting canonical {@link ActionEnvelope} messages.
const { status, sendAction, send, lastError } = useWebSocket({ url: wsEndpoint || "", sessionId, appId, onMessage: (msg) => console.log(msg),});
// Emit a canonical ActionEnvelope — wrapped into `{type: 'action', payload: envelope}`sendAction({ sessionId, type: "data:submit", payload: { action: "submit", data: { rating: 5 } }, stackIndex: 0,});Most application code should prefer GguiSession’s action render-prop — it builds the envelope, runs contract validation, and threads onBeforeAction middleware for you. useWebSocket.sendAction is the low-level path for custom transports.
useInvoke
Section titled “useInvoke”Channel 1 — conversational surface between the user and the wrapped-agent HTTP endpoint. Returns a Vercel-AI-SDK-style messages array plus a send function.
const { messages, send, status } = useInvoke({ agentUrl: "/api/chat" });See @ggui-ai/protocol/invoke types (ContentBlock, InvokeTurn) for the event shape. Channel 1 is product-mandatory (the default ggui serve product runs it) but protocol-optional — BYO chat frontends can plug directly into channels 2 + 3 and skip useInvoke.
Client-Side Tool System
Section titled “Client-Side Tool System”The tool system enables controllers to fetch and transform data at runtime. Tools are registered globally and executed via hooks.
useTool
Section titled “useTool”Execute a single tool and manage its loading/error state.
function UserProfile({ userId }: { userId: string }) { const { data, loading, error } = useTool({ config: { tool: "fetch", config: { endpoint: `/api/users/${userId}` }, }, });
if (loading) return <p>Loading...</p>; if (error) return <p>Error: {error.message}</p>; return <div>{data.name}</div>;}useBindings
Section titled “useBindings”Resolve multiple data bindings in topological order based on dependencies.
const { data, loading, errors } = useBindings({ bindings: { user: { tool: "auth", config: { field: "currentUser" } }, profile: { tool: "fetch", config: { endpoint: "/api/users/{user.id}/profile" }, dependsOn: ["user"], }, },});Built-in Tools
Section titled “Built-in Tools”| Tool | Description |
|---|---|
fetch | HTTP requests with caching and response extraction |
auth | Access authentication context data |
storage | Read from localStorage/sessionStorage |
chain | Sequential tool execution with {prev} interpolation |
transform | Data transformation (pick, omit, rename, flatten, map) |
merge | Combine multiple data sources (shallow, first-wins, deep) |
Testing Utilities
Section titled “Testing Utilities”Import from @ggui-ai/react/testing for Node.js test helpers:
import { setupMockTools, resolveBindingsForTest, validateComponentCode, validateControllerCode, createTestContext,} from "@ggui-ai/react/testing";
beforeEach(() => { setupMockTools({ auth: { id: "user-123", name: "Test User" }, fetch: { "/api/users": [{ id: 1, name: "Alice" }] }, storage: { theme: "dark" }, });});See the Testing cookbook for complete patterns.