← All posts

One Grammar Engine, Four Platforms: How Typlx Works on Chrome, Firefox, iOS & Android

When we started building Typlx, we made a decision that shaped the entire architecture: the grammar-checking logic would live in exactly one place. Not copied across platforms, not reimplemented for mobile — one shared engine that Chrome, Firefox, iOS, and Android all talk to. Here's how that design works and why we think it's the right approach for a privacy-first, open-source grammar checker.

The Problem with Platform-Native Grammar Checking

The conventional approach to building a grammar checker for multiple platforms looks like this: write the Chrome extension, then write a separate Firefox version, then write an iOS keyboard, then write an Android keyboard. Each platform has its own language (JavaScript for extensions, Swift for iOS, Kotlin for Android), its own text-access APIs, and its own UI paradigm. Most teams end up with four separate codebases that all do slightly different things.

This creates real problems:

  • Behavior diverges. The Chrome version fixes an issue in version 2.0, but the iOS version still has the old behavior because someone forgot to port the fix.
  • Testing multiplies. You're not testing one grammar checker — you're testing four. Every edge case has to be reproduced and verified on each platform.
  • Privacy surface grows. Each platform has its own configuration UI, its own way of storing API keys, its own set of network requests. More code paths means more places for something to go wrong.

We wanted to avoid all of this. The architecture we settled on treats grammar checking as a protocol, not an implementation.

The Grammar API: A Single Interface

At the center of Typlx is what we call the Grammar API — a simple, stateless interface that takes text and returns suggestions. It's not a server we run. It's a contract that describes how to talk to a grammar-checking LLM endpoint.

The interface is straightforward:

  • Input: a string of text and an optional context hint (email, code comment, formal document, etc.)
  • Output: a list of suggestions, each with a start position, end position, replacement text, and a brief explanation

This interface is implemented by the LLM backend the user configures — OpenAI's GPT models, Anthropic's Claude, a locally running Ollama instance, or any OpenAI-compatible API endpoint. Each platform's Typlx client knows how to format a request to this interface and how to parse the response. The grammar logic itself — what constitutes a correction, how to phrase an explanation — lives in the LLM prompt, not in platform-specific code.

Browser Extensions: Chrome and Firefox

The Chrome extension and Firefox add-on share the same JavaScript codebase. The WebExtensions API — the standard that both browsers implement — is close enough that we can ship the same source and only differ in the manifest. Chrome uses Manifest V3; Firefox still supports Manifest V2 with Manifest V3 in progress. We build both from the same source tree with a small build-time flag.

The browser extension's job is straightforward but technically involved:

  1. Detect editable text fields. The content script observes the DOM for focused <textarea> elements, contenteditable divs, and rich text editors (Gmail, Google Docs, Notion, and so on). Each has slightly different event behavior, which is where most of our edge-case testing lives.
  2. Debounce and extract text. We don't send a request on every keystroke. We wait until the user pauses — 600ms of inactivity by default — then extract the text of the current field or paragraph.
  3. Send to the configured LLM. The extracted text goes to whatever endpoint the user configured in the extension settings. This request goes directly from the user's browser to their LLM provider. It does not pass through any Typlx servers.
  4. Parse and render suggestions. The LLM response is parsed into a list of corrections. We render these as underlines in the text field and show a tooltip on hover with the suggested replacement and a brief explanation.
  5. Apply the correction. When the user clicks a suggestion, we replace the text in the field using the appropriate DOM API for that field type. This is the trickiest part: document.execCommand is deprecated, the Selection API behaves differently in different browsers, and some apps (notably Google Docs) intercept DOM mutations. We've spent significant time making this reliable across the major text editing surfaces.

The privacy property comes from step 3: we never touch the text. It goes from the browser to the LLM endpoint, and the extension only sees the suggestions that come back.

Mobile Keyboards: iOS and Android

Mobile keyboards are architecturally different from browser extensions, but the grammar API contract is the same. The keyboard extension on iOS and Android acts as a custom keyboard that intercepts text input, sends it to the configured LLM endpoint, and suggests corrections through the keyboard's suggestion bar.

iOS Keyboard Extension

The Typlx iOS keyboard is a UIKit-based keyboard extension that replaces the system keyboard when selected. It has access to the text document proxy, which gives it read and write access to the text field the user is typing in — the same text field that Mail, Notes, Safari, and any other iOS app uses.

A keyboard extension on iOS runs in a heavily sandboxed process. It cannot access the main app's memory, cannot make network requests by default (it requires explicit NSAllowsArbitraryLoads entitlements for full network access), and has a constrained memory budget. We designed around these constraints:

  • Grammar checking is triggered by punctuation and line breaks, not on every character — both for latency reasons and to reduce the request volume to the LLM endpoint.
  • API keys are stored in the shared App Group container so both the main app and the keyboard extension can access the user's configuration without requiring the user to configure the keyboard separately.
  • Suggestions appear in the keyboard's suggestion bar (the strip above the keys), matching the UX pattern iOS users expect from QuickType.

Android Keyboard

The Android keyboard is built on the InputMethodService API, which gives the keyboard access to the InputConnection — the channel between the keyboard and the app receiving input. Unlike iOS, Android keyboards have fewer sandbox restrictions, which gives us more flexibility but also more surface area to be careful about.

We chose to implement the Android keyboard as an Accessibility Service augment rather than a full custom keyboard replacement. This means Typlx works alongside the user's existing keyboard (Gboard, SwiftKey, whatever they prefer) rather than replacing it. Grammar suggestions appear as floating chips above the keyboard that the user can tap to apply — similar to how some Android apps show spelling suggestions.

This approach has a meaningful privacy advantage: the keyboard sees the text it's been asked to correct, but the user doesn't have to change their primary keyboard or trust Typlx with all their typing. It's a narrower access scope by design.

Configuration Sync and the Shared Protocol

A question we get from developers: how does a user configure their LLM API key once and have it work across all their devices?

The honest answer is: right now, they configure each client separately. The Chrome extension stores its key in browser local storage. The iOS and Android apps store theirs in the device keychain/secure storage. There's no central Typlx account because we deliberately didn't build server infrastructure to store user credentials.

We're working on a sync option that would let you store an encrypted configuration blob in your own cloud storage (Google Drive, iCloud, or a self-hosted option) and have all Typlx clients pull from it. This preserves the no-central-server property while making setup less tedious for multi-device users. It's on the roadmap but not yet shipped.

What This Architecture Gets Right

The single-engine design has delivered a few concrete benefits that weren't obvious at the start:

Prompt improvements propagate everywhere. The grammar-checking quality mostly lives in the system prompt we recommend sending to the LLM. When we improve that prompt — better handling of technical jargon, more natural rewrite suggestions, better behavior with non-native English — every platform gets the improvement automatically because they're all using the same API contract.

Privacy is structural, not a feature. We didn't have to build privacy controls into four separate platforms. The architectural decision to never run the grammar model ourselves means there's no server to lock down, no database to audit, no logging to disable. The privacy property emerges from the design.

The codebase stays auditable. If you want to understand what Typlx does, you can read the Chrome extension (the most-used client) and understand the protocol that everything else implements. You don't have to trust that the mobile apps behave the same way — they implement the same interface, which you can verify.

What We're Still Working On

A few areas where the current architecture has rough edges:

Rich text editors are hard. Google Docs in particular uses a virtual DOM that doesn't map cleanly to the underlying text. We have special handling for Docs that works most of the time, but we still see edge cases around collaborative editing and undo history. If you hit a bug in Docs specifically, filing an issue on GitHub helps us prioritize.

Mobile latency is noticeable. On a phone with a slower connection, waiting for an LLM response before showing suggestions creates a visible delay. We're experimenting with speculative suggestions and better streaming support, but mobile users will notice latency more than desktop users who are typing into a large text field.

Context window limits matter. Very long documents get truncated before we send them to the LLM. We send a sliding window around the cursor position rather than the entire document, which works well for sentence-level corrections but means we might miss issues that depend on content from earlier in the document.

Contributing

All four clients are open source. The Chrome/Firefox extension is at github.com/typlx/chrome-grammar-fix-extension. The iOS and Android repos are also in the typlx GitHub org.

If you're interested in contributing, the most valuable areas are: edge-case handling in specific web apps (Notion, Linear, Slack web, etc.), mobile performance improvements, and documentation for self-hosting LLM backends. The grammar prompt itself is also an area where community input has historically improved quality — if you notice systematic errors or missing suggestions, opening an issue with examples helps us tune the prompt.

The architecture is intentionally simple because simple is easier to audit. If you're building your own privacy-first text tool and want to use Typlx's approach as a reference, the protocol is documented in the repo's README and you're welcome to fork or adapt it under the MIT license.

Try Typlx — privacy-first grammar checking on every platform.

Install on Chrome Read the Source