Chrome Built-in AI APIs: Translator + Summarizer Implementation Guide
10 min read
Thu Feb 19 2026

Chrome Built-in AI APIs for Translator and Summarizer

Chrome now exposes task-focused built-in AI APIs that can run inference in browser contexts with explicit capability checks and model download flow. This guide focuses on two APIs: Translator and Summarizer, implemented through two reference tools I built for validation.

The engineering value is straightforward: low-latency UX for short text tasks, improved privacy for user-generated content, and cleaner progressive enhancement patterns than a mandatory server-side AI dependency.

Guide Scope

This is an implementation-first breakdown: support constraints, runtime checks, API creation patterns, and fallback architecture.

Current Support Snapshot (as of February 19, 2026)

Translator API

Stable in Chrome 138+ with browser-provided expert translation models.

Summarizer API

Stable in Chrome 138+ with Gemini Nano downloads and device constraints.

Context Limits

Desktop web pages/extensions supported; Android and Web Worker support are currently unavailable.

  • Translator API is available in Chrome 138+ stable and uses browser-provided expert models.
  • Summarizer API is available in Chrome 138+ stable and uses Gemini Nano model downloads.
  • Desktop support applies to web pages and extensions. Mobile support is currently not available for these APIs.
  • APIs are exposed to top-level windows and same-origin iframes; Web Worker support is currently not available.
  • Summarizer has additional device/runtime constraints (OS, memory, storage, and compute profile).
  • Feature availability is runtime-dependent and can return unavailable, downloadable, downloading, or available states.
  • Browser support can change quickly, so always check the latest Chrome docs before shipping.
APIChromeDesktopAndroidWeb WorkerModel Path
Translator138+YesNoNoBuilt-in expert model
Summarizer138+YesNoNoGemini Nano download

Implementation Flow You Should Use

  1. Run feature detection before showing controls.
  2. Call availability checks to understand whether the model is already present or needs download.
  3. Create API instances from explicit user interaction handlers.
  4. Dispose API objects after use to avoid unnecessary memory retention in long-lived sessions.
  5. Show progress/loading states and provide robust fallback UX for unsupported browsers.

Implementation Checklist

  • Detect API presence with `in window` checks.
  • Branch UI by availability state before triggering create().
  • Attach download progress monitor for first-run model fetches.
  • Destroy translator/summarizer instances after completion.

Working Demo

Use this embedded demo to test both APIs in your current browser. It defaults to streaming output and includes a toggle to switch between streaming and sync modes. If the browser/runtime is unsupported, availability will show as unavailable and actions stay disabled.

Status: Unknown

This demo only runs where the APIs are exposed. If unsupported, use the same fallback path described in the architecture section.

feature-detection.tsts
export function canUseTranslator() {
  return typeof window !== "undefined" && "Translator" in window;
}

export function canUseSummarizer() {
  return typeof window !== "undefined" && "Summarizer" in window;
}

export async function getTranslatorAvailability() {
  if (!canUseTranslator()) return "unavailable";
  return Translator.availability({
    sourceLanguage: "en",
    targetLanguage: "es",
  });
}

export async function getSummarizerAvailability() {
  if (!canUseSummarizer()) return "unavailable";
  return Summarizer.availability();
}

Gate create() with user activation

Run create() only from direct user actions (button click, form submit) to avoid activation-related failures.

user-activation-gate.tsts
export async function createSummarizerSafely() {
  if (!("Summarizer" in window)) throw new Error("unsupported");
  if (!navigator.userActivation?.isActive) {
    throw new Error("create() must run from a user gesture");
  }

  return Summarizer.create({
    type: "key-points",
    format: "markdown",
    length: "medium",
  });
}

Handling Streaming vs Sync Responses

Both APIs should be integrated with two output modes: sync andstreaming. The best pattern is a response-mode switch with automatic fallback to sync when streaming methods are unavailable.

  • Use streaming for progressive rendering and faster perceived response.
  • Use sync for simpler flow control and deterministic full outputs.
  • Keep sync fallback enabled even when streaming mode is selected.
streaming-response-handler.tsts
type OutputMode = "streaming" | "sync";

async function runWithOutputMode(
  mode: OutputMode,
  runSync: () => Promise<string>,
  runStream:
    | (() => Promise<AsyncIterable<string> | ReadableStream<string> | string>)
    | undefined,
  onNext: (value: string) => void,
) {
  if (mode === "streaming" && runStream) {
    const source = await runStream();
    if (typeof source === "string") {
      onNext(source);
      return;
    }

    let merged = "";
    if (Symbol.asyncIterator in source) {
      for await (const chunk of source as AsyncIterable<string>) {
        merged += chunk;
        onNext(merged);
      }
      return;
    }

    if ("getReader" in source) {
      const reader = (source as ReadableStream<string>).getReader();
      try {
        while (true) {
          const { done, value } = await reader.read();
          if (done) break;
          merged += value;
          onNext(merged);
        }
        return;
      } finally {
        reader.releaseLock();
      }
    }
  }

  onNext(await runSync());
}

Translator API in practice

  • Use source/target language values explicitly on create().
  • Track model download progress for first-run transparency.
  • Support both `translate()` and `translateStreaming()` response paths.
  • Dispose translator instances immediately after output generation.
translator-action.tsts
export async function translateText(
  input: string,
  targetLanguage: string,
  mode: "streaming" | "sync",
  onNext: (value: string) => void,
) {
  const translator = await Translator.create({
    sourceLanguage: "en",
    targetLanguage,
    monitor(monitor) {
      monitor.addEventListener("downloadprogress", (event) => {
        console.log("translator download", event.loaded);
      });
    },
  });

  try {
    await runWithOutputMode(
      mode,
      () => translator.translate(input),
      typeof translator.translateStreaming === "function"
        ? () => translator.translateStreaming!(input)
        : undefined,
      onNext,
    );
  } finally {
    translator.destroy?.();
  }
}

Summarizer API in practice

  • Choose summarization type/format/length based on product use-case.
  • Monitor first-run model download states.
  • Support both `summarize()` and `summarizeStreaming()` response paths.
  • Destroy summarizer instances once summary text is returned.
summarizer-action.tsts
export async function summarizeText(
  input: string,
  mode: "streaming" | "sync",
  onNext: (value: string) => void,
) {
  const summarizer = await Summarizer.create({
    type: "key-points",
    format: "markdown",
    length: "medium",
    monitor(monitor) {
      monitor.addEventListener("downloadprogress", (event) => {
        console.log("summarizer download", event.loaded);
      });
    },
  });

  try {
    await runWithOutputMode(
      mode,
      () => summarizer.summarize(input),
      typeof summarizer.summarizeStreaming === "function"
        ? () => summarizer.summarizeStreaming!(input)
        : undefined,
      onNext,
    );
  } finally {
    summarizer.destroy?.();
  }
}

Fallback Strategy for Real Users

  • If API support is missing, disable action buttons and show compatibility guidance.
  • Keep the input editable so users can still copy text to alternate tools.
  • Avoid hard failures; unsupported browsers should still see a usable interface.
  • Log support and availability states so you can monitor real-world adoption.
  • For broad compatibility, ship a hybrid path that can switch to server-side inference.
fallback-router.tsts
type AiPath = "browser" | "server";

export async function resolveAiPath() {
  const hasSummarizer = typeof window !== "undefined" && "Summarizer" in window;
  if (!hasSummarizer) return "server" as AiPath;

  const availability = await Summarizer.availability();
  if (availability === "available" || availability === "downloadable") {
    return "browser" as AiPath;
  }

  return "server" as AiPath;
}

Primary References