useSession
Lifecycle-only React adapter for the chat-shaped createSession() primitive in @web-ai-sdk/prompt. Each useSession call owns one underlying LanguageModelInstance, with independent history, system prompt, sampling, and lifecycle. The hook handles feature detection, async create readiness, destroy-on-unmount, and recreate-on-options-change. It does not track response, history, or streaming status; iterate session.sendStreaming() yourself and keep UI state in your own component.
For ask-and-display flows (embeds, widgets, single-question UIs) prefer usePrompt; it uses isolated one-shot prompts and may keep a warm base session for same-shape calls.
import { useSession } from "@web-ai-sdk/prompt/react";import { useState } from "react";
export function Chat({ persona }: { persona: string }) { const { status, session } = useSession({ systemPrompt: persona, samplingMode: "balanced", });
const [response, setResponse] = useState(""); const [streaming, setStreaming] = useState(false);
if (status === "unavailable" || !session) return null;
const send = async (text: string) => { setStreaming(true); setResponse(""); try { let buffer = ""; for await (const delta of session.sendStreaming(text)) { buffer += delta; setResponse(buffer); } } finally { setStreaming(false); } };
return ( <div> <p>{response}</p> <form onSubmit={(e) => { e.preventDefault(); send("Hello"); }}> <button type="submit" disabled={streaming}>Send</button> <button type="button" onClick={() => session.abort()}>Stop</button> </form> </div> );}session.sendStreaming(text) yields deltas (each chunk is the new text since the last yield). Accumulate into your own response state for a typewriter effect. The hook stays out of the streaming loop entirely.
Independent chats
Section titled “Independent chats”Two useSession calls with identical options get two independent underlying instances. In a chat app with N agents in the same mode, each pane owns its own history, abort, and destroy lifecycle. Current Chrome / Edge builds may still serialize token generation FIFO across sessions because the underlying on-device model is single-instance.
function ChatList({ agents }: { agents: Agent[] }) { return agents.map((agent) => <ChatPane key={agent.id} agent={agent} />);}
function ChatPane({ agent }: { agent: Agent }) { // Each ChatPane owns its own session even when systemPrompt / // samplingMode are identical. const { session } = useSession({ systemPrompt: agent.systemPrompt, samplingMode: agent.samplingMode, }); // …}Forking with clone() (agents / multi-task)
Section titled “Forking with clone() (agents / multi-task)”session is the vanilla Session, so session.clone() is available in React too. For agents that run many tasks against one persona, keep the useSession base warm (system prompt only) and clone it per task so each run gets fresh history without re-parsing the system prompt. See Session resilience for the full rationale.
const { session } = useSession({ systemPrompt });
const runTask = async (input: string) => { if (!session) return; const turn = await session.clone(); // fresh history, base stays warm try { for await (const delta of turn.sendStreaming(input)) render(delta); } finally { turn.destroy(); }};Destroying a clone never affects the base session. The hook still owns the base session’s lifecycle (destroyed on unmount / option change); clones you create are yours to destroy.
Return shape
Section titled “Return shape”interface UseSessionReturn { status: "loading" | "ready" | "unavailable"; error: Error | null; session: Session | null; // null until status === "ready"}loading: the underlyingLanguageModel.create()is in flight.sessionisnulluntil creation succeeds.ready:sessionis non-null. Iteratesession.sendStreaming(...), callsession.send(...),session.abort(), orsession.destroy()(the hook also destroys on unmount).unavailable: API missing,enabled: false, or native creation failed. Render a fallback; creation failures are exposed onerror.
The hook waits for native creation before reporting "ready". If LanguageModel.create() rejects, the hook destroys the wrapper, reports "unavailable", and stores the creation error in error.
Recreating the session
Section titled “Recreating the session”The hook recreates the underlying instance whenever a primitive option changes (systemPrompt, samplingMode, temperature, topK, language, enabled). Object options (expectedInputs, expectedOutputs, createOptions) participate in the dependency check by reference; memoize them or accept the recreate cost.
const expected = useMemo<LanguageModelExpectedInput[]>( () => [{ type: "text", languages: ["en"] }], [],);const { session } = useSession({ systemPrompt, expectedInputs: expected });Disabled state
Section titled “Disabled state”Pass enabled: false to skip session creation entirely (status stays "unavailable"). Flipping it back to true creates the session on the next render. Useful when the session depends on user opt-in.
Seeding multi-turn context
Section titled “Seeding multi-turn context”Pass createOptions.initialPrompts to restore a prior conversation:
const { status, session } = useSession({ systemPrompt: "You are a helpful assistant.", createOptions: { initialPrompts: [ { role: "system", content: "You are a helpful assistant." }, { role: "user", content: "Earlier turn from storage." }, { role: "assistant", content: "Earlier reply." }, ], },});
if (status !== "ready" || !session) return null;The seeded turns become real context for the model. UI history lives in your own component state.
Reference
Section titled “Reference”import type { UseSessionOptions, UseSessionReturn, SessionStatus } from "@web-ai-sdk/prompt/react";
type SessionStatus = "loading" | "ready" | "unavailable";
interface UseSessionOptions extends CreateSessionOptions { enabled?: boolean; // default: true; skip session creation when false // Inherited from CreateSessionOptions: systemPrompt?: string; samplingMode?: "most-predictable" | "predictable" | "balanced" | "creative" | "most-creative"; temperature?: number; topK?: number; language?: string; supportedLanguages?: readonly string[]; expectedInputs?: LanguageModelExpectedInput[]; expectedOutputs?: LanguageModelExpectedOutput[]; createOptions?: LanguageModelCreateOptions;}
interface UseSessionReturn { status: SessionStatus; error: Error | null; // creation error, if any session: Session | null; // null until status === "ready"}
interface Session { readonly destroyed: boolean; readonly contextWindow?: number; readonly contextUsage?: number; send(input: string, options?: SessionSendOptions): Promise<string | null>; sendStreaming(input: string, options?: SessionSendOptions): AsyncIterable<string>; // yields deltas, not cumulative abort(): void; clone(options?: { signal?: AbortSignal }): Promise<Session>; onContextOverflow(listener: () => void): () => void; destroy(): void;}
declare const useSession: (options?: UseSessionOptions) => UseSessionReturn;Source: packages/prompt/src/react/index.ts.