@web-ai-sdk/proofreader
web-ai-sdk building block for the Web’s Built-in Proofreader API. Corrects grammar, spelling, and punctuation and returns per-issue corrections with offsets, plus session reuse and opt-in result caching.
Status
Section titled “Status”The Proofreader API is in an origin trial in Chrome 141 to 145, behind chrome://flags/#proofreader-api-for-gemini-nano on localhost (chrome://flags/#optimization-guide-on-device-model must also be enabled). In Edge it’s a developer preview in Canary/Dev 142+ behind “Proofreader API for Phi mini”. On any other browser this library is a no-op for the React hook (it stays in "unavailable"). The vanilla proofread() throws ProofreaderUnavailableError so callers can branch explicitly.
The API is English-only today. expectedInputLanguages accepts an array, but a request for an unsupported language causes the underlying create() to reject (surfaced here as ProofreaderUnavailableError); pass ["en"] or omit it.
Install
Section titled “Install”pnpm add @web-ai-sdk/proofreader# or: npm i @web-ai-sdk/proofreader / bun add @web-ai-sdk/proofreaderThe React adapter ships as a subpath export, with no extra install. react is a peer dependency only when you import the /react entry.
Vanilla TypeScript / DOM
Section titled “Vanilla TypeScript / DOM”import { proofread } from "@web-ai-sdk/proofreader";
const result = await proofread({ input: "I seen him yesterday at the store, and he bought two loafs of bread.", expectedInputLanguages: ["en"],});
console.log(result.output?.correctedInput);for (const c of result.output?.corrections ?? []) { console.log(c.startIndex, c.endIndex, "→", c.correction);}result.output is null when the input is empty; otherwise correctedInput is the fully corrected text and corrections is the list of per-issue edits with offsets into the original input.
import { useProofreader } from "@web-ai-sdk/proofreader/react";
export function GrammarCheck({ text }: { text: string }) { const { status, output } = useProofreader({ input: text });
if (status === "unavailable") return null; if (status === "loading") return <p>Checking…</p>; return <p>{output?.correctedInput}</p>;}State machine: idle | loading | done | unavailable. There is no streaming; proofread() resolves once. fromCache is true when the result came back without invoking the model.
proofread(options): Promise<ProofreadResult>
Section titled “proofread(options): Promise<ProofreadResult>”interface ProofreadOptions { input: string; expectedInputLanguages?: readonly string[]; monitor?: (m: CreateMonitor) => void; cache?: "session" | "local" | { get, set }; cacheKey?: string; signal?: AbortSignal;}
interface ProofreadCorrection { startIndex: number; // inclusive offset into the original input endIndex: number; // exclusive offset into the original input correction: string; // suggested replacement type?: string; // not emitted by Chrome's current build explanation?: string; // not emitted by Chrome's current build}
interface ProofreadOutput { correctedInput: string; corrections: ProofreadCorrection[];}
interface ProofreadResult { output: ProofreadOutput | null; cached: boolean;}isAvailable(): boolean
Section titled “isAvailable(): boolean”Feature-detect helper.
checkAvailability(options?): Promise<ProofreaderAvailability | null>
Section titled “checkAvailability(options?): Promise<ProofreaderAvailability | null>”Forwards to the spec’s availability() call. Returns null if the global is missing or the call throws.
clearProofreaderSessions(): void
Section titled “clearProofreaderSessions(): void”Drop every cached proofreader session. Sessions live for the tab lifetime by default.
Session cache controls
Section titled “Session cache controls”configureProofreaderCache({ max }) bounds the internal warm Proofreader session cache (default 8). clearProofreaderSessions() drops every warm session, and clearProofreaderSession({ expectedInputLanguages }) drops one matching proofreader configuration.
Rendering corrections
Section titled “Rendering corrections”The corrections offsets index into the original input, so you can highlight each error in place by slicing between offsets:
let cursor = 0;const spans: Array<{ text: string; error: boolean }> = [];for (const c of output.corrections) { if (c.startIndex > cursor) spans.push({ text: input.slice(cursor, c.startIndex), error: false }); spans.push({ text: input.slice(c.startIndex, c.endIndex), error: true }); cursor = c.endIndex;}if (cursor < input.length) spans.push({ text: input.slice(cursor), error: false });License
Section titled “License”MIT © Beto Muniz