Chrome's Writer API: Generate Free, On-Device AI Content in Your Extension

Your extension helps users write — reviews, emails, support tickets, bios. They want a “draft this for me” button. You want to ship it without paying per-token cloud bills, without routing private text through a third-party server, and without asking users to paste an API key.
Chrome’s Writer API makes that possible. Starting in Chrome 137, you call Writer.create(), feed it a writing task, and get polished text back — generated on-device by Gemini Nano. No keys. No billing. No data leaving the browser.
If your extension touches user-generated content, this is the AI API you ship next.
Why the Chrome Writer API Matters for Extension Developers
Adding text generation to an extension today means picking your poison:
- Cloud API (OpenAI, Anthropic) — per-token costs that scale linearly with users
- Self-hosted model — infrastructure overhead, latency, and a privacy policy rewrite
- Template-based generation — rigid, robotic output that users immediately recognize as canned
The Writer API eliminates all three. Gemini Nano is shared across Chrome itself — downloaded once, used by every site and extension. Your extension gets free, private, instant content generation with no backend, no billing dashboard, and no privacy disclosure.
The killer feature for extensions: on-device means you can generate drafts based on the user’s Gmail, CRM notes, or internal docs without ever transmitting that data anywhere.
Hardware Requirements: What Your Users Need
Before building, know the minimum specs:
| Requirement | Specification |
|---|---|
| OS | Windows 10/11, macOS 13+, Linux, ChromeOS (Chromebook Plus) |
| Storage | 22 GB free (model auto-removes if space drops below 10 GB) |
| RAM | 16 GB with 4+ cores (CPU) or >4 GB VRAM (GPU) |
| Network | Unmetered connection for initial model download |
| Browser | Chrome 137+ |
Not yet supported: Android, iOS, mobile Chrome, Firefox, Safari, Web Workers.
Pro tip: The Gemini Nano model is shared browser-wide. If another extension or site already triggered the download, your users pay zero install cost.
Quick Start: Generate Text in 3 Steps
1. Check Feature Availability
if (!("Writer" in self)) {
console.log("Writer API not supported in this browser");
// Fallback to cloud API or disable feature
}
const availability = await Writer.availability();
// Returns: "available" | "downloadable" | "downloading" | "unavailable"2. Create a Writer Instance
const writer = await Writer.create();If the model needs downloading, show progress to the user:
const writer = await Writer.create({
monitor(m) {
m.addEventListener("downloadprogress", (e) => {
console.log(`Downloading AI model: ${Math.round(e.loaded * 100)}%`);
});
},
});3. Generate Content
const draft = await writer.write("Write a professional reply to this customer complaint", {
context: "The customer is upset about a delayed shipment",
});
console.log(draft);Three steps. No credentials. No SDK install. No billing page.
Configuration: Control Tone, Format, and Length
The Writer.create() method accepts options that shape the output without prompt engineering:
const writer = await Writer.create({
tone: "formal", // "formal" | "neutral" (default) | "casual"
format: "markdown", // "markdown" (default) | "plain-text"
length: "medium", // "short" (default) | "medium" | "long"
sharedContext: "This is for a SaaS customer support team",
});| Option | Values | Default | What It Does |
|---|---|---|---|
tone | formal, neutral, casual | neutral | Controls writing style formality |
format | markdown, plain-text | markdown | Output format |
length | short, medium, long | short | Controls output verbosity |
sharedContext | Any string | — | Background context applied to all .write() calls |
sharedContext is your secret weapon. Set it once when creating the writer, and every generation inherits that context without burning tokens repeating it in each prompt.
Real Extension Use Cases
The Writer API isn’t a demo toy. Here’s what you can ship today:
AI Review Drafting
Help users write Chrome Web Store reviews, product reviews, or testimonials:
const writer = await Writer.create({
tone: "casual",
length: "medium",
sharedContext: "Writing a product review for the Chrome Web Store",
});
const review = await writer.write(
"Write a positive review about a password manager extension that saved me time",
{ context: "The extension auto-fills forms and syncs across devices" }
);Smart Email Composer
Draft professional emails based on bullet points:
const writer = await Writer.create({
tone: "formal",
format: "plain-text",
length: "medium",
});
const email = await writer.write(
"Write a follow-up email to a client about a delayed project deliverable",
{ context: "Project was due last Friday. New ETA is next Wednesday. Client is a VP." }
);Support Ticket Helper
Generate well-structured bug reports from rough user descriptions:
const writer = await Writer.create({
tone: "neutral",
format: "markdown",
length: "long",
sharedContext: "Generating a support ticket for a web application",
});
const ticket = await writer.write(
"Create a bug report from this description: login page crashes when I click submit with empty fields"
);Bio & Introduction Generator
Help users draft professional intros for portfolios or social profiles:
const writer = await Writer.create({
tone: "casual",
length: "short",
});
const bio = await writer.write(
"Write a short professional bio",
{ context: "Software engineer, 5 years experience, specializes in React and Node.js, based in San Francisco" }
);Streaming Responses for Better UX
For any output longer than a sentence, stream it so users see text appearing in real-time:
const stream = writer.writeStreaming(
"Write a detailed product comparison",
{ context: "Comparing Notion vs Obsidian for personal knowledge management" }
);
for await (const chunk of stream) {
outputElement.textContent = chunk;
}Golden rule: Always stream for medium or long outputs. Users staring at a blank box while AI generates behind the scenes will think your extension froze.
Language Support
The Writer API supports multilingual generation with explicit language configuration:
const writer = await Writer.create({
expectedInputLanguages: ["en"], // Input will be in English
expectedContextLanguages: ["en"], // Context will be in English
outputLanguage: "es", // Generate output in Spanish
});
const result = await writer.write("Write a welcome message for new users");
// Output will be in SpanishUse BCP 47 language tags (en, es, fr, ja, vi, etc.). This is cleaner than appending “respond in Spanish” to every prompt.
Reusing Writers and Cleanup
A single Writer instance can generate multiple pieces of content:
const writer = await Writer.create({ tone: "formal", length: "short" });
const intro = await writer.write("Write an introduction for a case study");
const conclusion = await writer.write("Write a conclusion for a case study");
const cta = await writer.write("Write a call-to-action for a case study");When done, clean up:
writer.destroy();Always destroy writers when you’re done. Each instance consumes memory. Leak them and your extension will degrade browser performance — earning one-star reviews.
Cancelling In-Progress Generation
const controller = new AbortController();
const writer = await Writer.create({ signal: controller.signal });
// Cancel if user navigates away or clicks "stop"
controller.abort();
writer.destroy();Access Control: iframes and Cross-Origin
By default, the Writer API is only available in:
- Top-level windows
- Same-origin iframes
To enable it in a cross-origin iframe, the parent page must set a Permissions Policy:
<iframe src="https://other-origin.com" allow="writer"></iframe>Not available in: Web Workers, Service Workers.
For extensions, this mostly matters if your extension injects iframes. Content scripts running in the main page context work fine.
The Graceful Fallback Pattern
Not every user will have Gemini Nano. Here’s the production pattern:
async function generateContent(prompt, context) {
// Check if Writer API exists
if (!("Writer" in self)) {
return fallbackToCloudAPI(prompt, context);
}
const availability = await Writer.availability();
if (availability === "available") {
const writer = await Writer.create({ tone: "neutral", length: "medium" });
const result = await writer.write(prompt, { context });
writer.destroy();
return result;
}
if (availability === "downloadable" || availability === "downloading") {
// Show download progress to user
const writer = await Writer.create({
tone: "neutral",
length: "medium",
monitor(m) {
m.addEventListener("downloadprogress", (e) => {
showProgressBar(Math.round(e.loaded * 100));
});
},
});
const result = await writer.write(prompt, { context });
writer.destroy();
return result;
}
// Hardware doesn't meet requirements — fall back
return fallbackToCloudAPI(prompt, context);
}The 86% rule applies here. Most users decide within minutes whether your extension is worth keeping. If the “draft for me” button silently fails with no fallback, you’ve lost them.
Writer vs. Rewriter: When to Use Which
Chrome ships two Writing Assistance APIs. Here’s the split:
| Scenario | Use Writer | Use Rewriter |
|---|---|---|
| Generate text from scratch | Yes | — |
| Rewrite existing user text | — | Yes |
| Draft an email from bullet points | Yes | — |
| Make existing text more formal | — | Yes |
| Create a review from a rating | Yes | — |
| Shorten a long paragraph | — | Yes |
Rule of thumb: If you’re starting from a blank page, use Writer. If you’re improving existing text, use Rewriter.
Current Status & What’s Next
| Detail | Status |
|---|---|
| API Status | Origin Trial (Chrome 137–148) |
| Model | Gemini Nano (on-device) |
| Registration | Chrome Origin Trials |
| Spec | Writing Assistance APIs |
The Writer API is in origin trial now — you can ship it to real users today. The API surface may evolve before stable release, so keep your fallback patterns tight and pin your origin trial tokens.
Important: Using the origin trial requires acknowledging Google’s Generative AI Prohibited Uses Policy.
Key Takeaways
- Zero cost, zero latency, zero privacy risk — Gemini Nano runs entirely on-device
- Three steps from zero to AI-powered content generation in your extension
- Built-in tone, format, and length controls — no prompt engineering hacks needed
sharedContexteliminates repetition — set once, apply to every generation- Always implement fallbacks — not every device supports on-device AI yet
- Stream long outputs — never leave users staring at a blank box
- Clean up writers — memory leaks kill reviews
The Writer API is the easiest path to shipping AI content generation in a Chrome extension. No billing, no backend, no excuses.
Ready to build? Register for the Origin Trial, start generating, and when your AI-powered extension is ready for the Chrome Web Store, AppBooster has the tools to help you launch, optimize, and grow.
Share this article
Build better extensions with free tools
Icon generator, MV3 converter, review exporter, and more — no signup needed.
Related Articles
Building Accessible Chrome Extensions: Keyboard, Screen Reader, and WCAG Compliance
26% of US adults have disabilities. Make your Chrome extension accessible with focus traps, ARIA, keyboard nav, and WCAG 2.1 AA compliance.
I Built the Same Chrome Extension With 5 Different Frameworks. Here's What Actually Happened.
WXT vs Plasmo vs CRXJS vs Extension.js vs Bedframe. Real benchmarks, honest opinions, and the framework with 12K stars that's quietly dying.
5 Best Email Marketing Services to Grow Your Chrome Extension (2026)
Compare the top email marketing platforms for SaaS and Chrome extension developers. MailerLite, Mailchimp, Brevo, ActiveCampaign, and Drip reviewed.