Skip to content

useSession

useSession is the React adapter for createSession(). Each call owns one native instance. The hook detects support, waits for creation, destroys on unmount, and recreates after option changes.

Pick an image and click “Describe”. The session sends one message with a text part and an image part, then streams the description back. The selected File is a browser-native image value; the SDK forwards it to the model unchanged. See Multimodal input for the content-part contract.

The hook does not track responses, history, or streaming status. Keep that state in your component.

For one-shot interfaces, use usePrompt. It isolates each prompt and can reuse a prepared base session.

When enabled, mounting the hook starts native creation. The hook stays "loading" and keeps session null until creation finishes.

Before shipping, review the Production checklist for initial instructions, base-plus-clone tasks, destruction, safe rendering, progress, and reversible UI.

useSession owns its session directly, outside the shared warm cache. For how ask() and the task packages manage warm sessions, see the Session lifecycle guide.

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. Accumulate them in your response state. The hook does not manage the streaming loop.

Two hook calls with identical options get independent native instances. Each component owns its history, abort, and destroy lifecycle. Scheduling across instances is browser-defined.

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 it supports clone(). Keep one base with the system prompt and clone it for each independent task. See Session resilience.

const { session } = useSession({ systemPrompt });
const [response, setResponse] = useState("");
const runTask = async (input: string) => {
if (!session) return;
// Native base creation started when the hook mounted; clone from the ready base.
const turn = await session.clone();
let nextResponse = "";
try {
for await (const delta of turn.sendStreaming(input)) {
nextResponse += delta;
setResponse(nextResponse);
}
} finally {
turn.destroy();
}
};
return <p>{response}</p>;

Destroying a clone does not affect its base. The hook manages the base lifecycle. You must destroy clones that you create.

interface UseSessionReturn {
status: "loading" | "ready" | "unavailable";
error: Error | null;
session: Session | null; // null until status === "ready"
}
  • loading: the underlying LanguageModel.create() is in flight. session is null until creation succeeds.
  • ready: session is non-null. Iterate session.sendStreaming(...), call session.send(...), session.abort(), or session.destroy() (the hook also destroys on unmount).
  • unavailable: API missing, enabled: false, or native creation failed. Render a fallback; creation failures are exposed on error.

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.

The hook recreates the instance when a primitive option changes. It compares object options by reference; memoize them to avoid recreation.

const expected = useMemo<LanguageModelExpectedInput[]>(
() => [{ type: "text", languages: ["en"] }],
[],
);
const { session } = useSession({ systemPrompt, expectedInputs: expected });

Pass enabled: false to skip creation. The status stays "unavailable". Setting it to true creates the session on the next render.

Pass createOptions.initialPrompts to restore a prior conversation:

const { status, session } = useSession({
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;

Seeded turns become model context. Keep UI history in component state. Use either systemPrompt or createOptions.initialPrompts, not both.

If you supply both, initialPrompts takes precedence. The SDK warns once that it ignored systemPrompt.

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 | LanguageModelMessage[], options?: SessionSendOptions): Promise<string | null>;
sendStreaming(input: string | LanguageModelMessage[], options?: SessionSendOptions): AsyncIterable<string>; // yields deltas, not cumulative
append(messages: LanguageModelMessage[], options?: { signal?: AbortSignal }): Promise<void>; // context without a turn
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.