# InboxSDK: building Gmail extensions without touching the DOM yourself

> InboxSDK is a JavaScript library for building applications inside Gmail with browser extensions. It gives you a declarative API and handles the DOM manipulation, absorbs the awkward cases such as multiple inboxes and the preview pane, and ships new versions as Gmail itself ships, often before the change is fully rolled out. It is developed by Streak, who use it for their own product.

**InboxSDK/InboxSDK** — The InboxSDK lets you build apps for Gmail.

- Repository: https://github.com/InboxSDK/InboxSDK
- Stars: 845 · Forks: 65
- Language: TypeScript
- License: NOASSERTION
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/inboxsdk-inboxsdk

## What the SDK is, and the three claims it makes

InboxSDK is a JavaScript library for building apps inside of Gmail with browser extensions, published on npm as @inboxsdk/core.

The first claim is no DOM hacking. It exposes a high level, declarative API and handles all the DOM manipulation in a performant way. That is the substance of the library, because Gmail's DOM is not a stable public interface and it changes without notice.

The second claim is that it handles edge cases: multiple inboxes, conversation mode on and off, personal versus workspace accounts, the preview pane, fullscreen compose, popouts and themes. Each of those is a variation in Gmail's layout that a hand-rolled extension would have to handle by hand, and handling them incorrectly is the usual reason a Gmail extension works for one user and breaks for the next.

The third claim is constant updates, with new versions released as Gmail updates, often before their changes are fully rolled out. That last clause is the interesting one, and it cuts both ways. It means the SDK is not waiting for a stable release of a Gmail change before adapting to it, which is good for the extension author. It also means the SDK is tracking a moving target, so your extension is only as stable as the compatibility between the SDK version and the Gmail version in front of the user.

The project is developed by Streak, who use the InboxSDK to build their own product, and they share it to collaborate with others on integrating with Gmail and with each other's extensions. The site is inboxsdk.com and the documentation is at inboxsdk.github.io/inboxsdk-docs with its own repository.

The most recent releases are v2.2.31 on 2026-10-16, v2.2.30 on 2026-10-08 and v2.2.29 on 2026-10-05, with the last push to the repository on 2026-10-21. That cadence, three patch releases in a fortnight, is a direct measure of how fast Gmail moves.

## Getting started: the package, the example extension and the app id

The installation steps are deliberately short, and the README defers the detail to the Quickstart in the docs.

You use the @inboxsdk/core package on npm. You set up the project the way the example extension is set up, and the README points at the hello-world repository as the model. And you generate an app id from a registration page.

That third step is the one with operational significance. An app id ties your extension to a registered application, and the SDK's load call needs it, so the sequence is npm install, project setup, then generate the id.

The usage example is the smallest interesting thing the SDK does, adding a button to the Gmail compose window:

```javascript
import * as InboxSDK from '@inboxsdk/core';

InboxSDK.load(2, 'YOUR_APP_ID_HERE').then((sdk) => {
  // the SDK has been loaded, now do something with it!
  sdk.Compose.registerComposeViewHandler((composeView) => {
    // a compose view has come into existence, do something with it!
    composeView.addButton({
      title: 'My Nifty Button!',
      iconUrl: 'https://example.com/foo.png',
      onClick(event) {
        event.composeView.insertTextIntoBodyAtCursor('Hello World!');
      },
    });
  });
});
```

The structure of that snippet is the whole API shape in miniature. load takes a version number and your app id and returns a promise for the SDK object. You then register a handler for a view, in this case the compose view, and the handler is called when that view comes into existence. Inside the handler you add a button, and the button's onClick receives an event that carries the compose view, so you can insert text at the cursor.

The registration-then-handler pattern is what makes the library work at all. Gmail creates and destroys compose windows constantly as you click around, and a handler registered once runs each time a matching view appears.

## The examples directory, and how the project expects you to work

The repository's examples directory is the closest thing to a specification of what the SDK can do, and the list is long: hello-world, add-route-view-sections, app-menu, attachment-card, browser-test, compose-dropdown-button, compose-stream, compose-tooltip-button, contact-hover-example, custom-thread-list, custom-view, header-support, link-popovers, modal-example, mole-example, nav-menu, open-compose, override-edit-subject, search-results, search-suggestions, search-term and sidebar-contact-example, plus a Basic InboxSDK Safari extension.

The README's development cycle essentials say to run yarn to install dependencies and yarn start to start the automatic builder, then load examples/app-menu/ as an unpacked extension into Google Chrome. The Chrome Extensions Reloader is supported, and if it is detected it triggers whenever any changes are made to any SDK files, so you do not have to click reload yourself on every change, though you still need to refresh Gmail.

The contribution guidance is more interesting than the mechanics. When working on the InboxSDK you should test with one of the example extensions in the examples directory. If you are working on a specific feature, find an example that uses it, or edit a relevant example to use it. If you are adding a new feature, make one of the examples use the new feature, and the new feature should be easily usable in isolation in an example extension and must not depend on Streak, including Streak's CSS. Any new feature that adds elements controlled by the extension ought to be styled reasonably by the InboxSDK without requiring the extension to include its own non-trivial CSS.

That is a demanding review standard and it is worth understanding why. The examples are the documentation for anyone integrating with Gmail, and if a feature can only be demonstrated inside one product, nobody else can use it.

## Technology choices: TypeScript over Flow, and a picky relationship with React

The README documents two technology decisions with reasoning attached, and both are the kind of thing you only write down if you have been through the alternative.

The first is TypeScript. The project has moved its Flow code over to TypeScript, and the README is candid that there are still a lot of rougher types from the Flow days, with an invitation to tighten them up if you are working around them. That is a migration described honestly, including the fact that the type coverage is uneven rather than pretending it is complete.

The second is React, and the reasoning is the interesting part. React is done for some user interfaces, but there is a complication to using it generally: the SDK often needs to integrate with elements from Gmail itself or from other instances of the InboxSDK. React only works well for the case where there are a lot of elements created and managed by the InboxSDK itself. There are many places where the SDK has to integrate with outside elements, and most of the additions into the page are pretty simple in DOM terms, so they are picky about where React is used.

That is a defensible engineering position rather than an ideological one. A React tree that owns the page cannot easily coexist with Gmail's own markup, and a keyboard-and-menu insertion into an existing DOM is trivial without it. The SDK uses React where it owns the elements and imperative DOM work where it does not.

The repository layout matches that: src/ for the implementation, packages/ for the published packages, types/ for type definitions, live/ and test/ for the test harnesses, tools/ for build tooling, and a gulpfile.babel.ts driving the build with Gulp, alongside Babel, Jest, ESLint, Prettier and Stylelint configuration.

## The hard problem: Gmail ships to users unevenly

The last section of the README is about fixing unreproducible bugs, and it explains the hardest constraint in building against Gmail.

Gmail frequently delivers rolling updates to users, so that a small percent of users run different versions of Gmail than most people. These different versions may visually appear the same but contain internal differences, in HTML structure and in ajax request and response formats, that cause compatibility issues for the InboxSDK.

That is a genuinely uncomfortable position to be in as a library. The same SDK code runs against several Gmail implementations at once, and the ones that look identical behave differently. A bug report from one user may not reproduce for anyone else, because the reporter's Gmail is a different build.

The mitigation the README describes is remote error logging that makes it obvious whenever Gmail's HTML structure or ajax formats are not what was expected. The example given is a querySelector call that must return an element. The guidance is either to import and use querySelectorOrFail.ts, which throws an error with a useful message if no element is found, or to handle a null return explicitly with code like this:

```js
const insertionPointEl = el.querySelector('.foo .bar');
if (!insertionPointEl) {
  const err = new Error('Could not find FOO element');
  driver.g
```

The pattern is more valuable than the snippet. A plain querySelector returning null, unchecked, becomes a TypeError somewhere further along, and by the time you read the stack trace you have no idea that the real cause was a Gmail DOM change. querySelectorOrFail turns it into an error at the point of the assumption, with a message that names the element.

Remote error logging for this class of failure is a real design constraint rather than a nice-to-have, and it explains a good deal about the shape of the library.

## Limits: tied to one product, and a licence with two options

Several limits follow from what this library is.

The first is that it is only useful inside Gmail. Everything the SDK does, the view handlers, the button registration, the text insertion, depends on Gmail's own DOM and on the browser extension APIs Gmail exposes to injected content. There is no version of this that runs in a standalone web page, and no version of it that works against another mail provider.

The second is the version coupling. The SDK releases track Gmail releases, and your extension runs against whatever Gmail version the user has. A user on an older rollout, which the README notes is a small percent but a real group, can get behaviour that no developer has reproduced locally. That is the shape of the risk: it is not that the SDK is unstable, it is that the environment underneath it is.

The third is the compatibility of Gmail's own extension model. A Gmail add-on runs with different privileges than a normal extension, and the ecosystem is not large enough to assume every capability is available. The README's edge-case list is really a list of places where Gmail's behaviour differs, and each one is a place where your extension can behave differently than you expect.

The fourth is the licence, which is a genuine choice rather than an accident. This release is distributed under the terms of both the MIT licence and the Apache License version 2.0, and the README says it may be used and redistributed according to the terms of either licence, and that these are permissive licences that do not require modifications or embedding applications to be open source themselves. The repository carries LICENSE-APACHE.txt, LICENSE-MIT.txt and COPYRIGHT.txt for the details. The dual licensing is unusual and useful: some organisations have a policy against Apache-2.0's patent grant and others against MIT in certain contexts, and offering both removes a procurement conversation.

The fifth is the maintenance reality behind the contribution guidance, which says it plainly: many features require ongoing maintenance to support as Gmail changes, and the maintainers may not want to commit to supporting every requested feature. That is the honest reason the SDK's surface area, while wide, is not infinite.

## Conclusion

Adopt InboxSDK when you need to extend Gmail with your own interface, because it absorbs the DOM coupling, the preview pane and the conversation-mode differences that make Gmail extensions brittle. Do not adopt it for anything outside a Gmail tab, since it depends on Gmail's internal structure and on the extension APIs Gmail exposes. Verify first that the features you need are supported in the version you install, since the SDK tracks Gmail's own releases, and that you register an app id and set up the project before you expect anything to render.

## FAQ

### What does the InboxSDK do and what are its main features?

InboxSDK is a JavaScript library for building apps inside Gmail with browser extensions. Its three stated features are no DOM hacking, exposing a high level declarative API that handles the DOM manipulation, handling edge cases such as multiple inboxes, conversation mode, personal versus workspace accounts, the preview pane, fullscreen compose, popouts and themes, and constant updates with new versions released as Gmail updates, often before their changes are fully rolled out.

### How do I get started with the InboxSDK?

Install the @inboxsdk/core package from npm, set up the project the way the hello-world example extension is set up, and generate an app id from the SDK's registration page. Then call InboxSDK.load with the version and your app id, register a view handler such as registerComposeViewHandler, and add UI elements to the view you receive.

### How does the InboxSDK handle Gmail versions rolling out unevenly?

The README notes that Gmail delivers rolling updates so a small percent of users run different versions than most people, and that these may look the same but differ internally in HTML structure and ajax formats. The guidance is to add remote error logging that surfaces when those structures are not what was expected, and to either use querySelectorOrFail, which throws a useful error when no element is found, or handle a null return explicitly.

## Sources

- [InboxSDK/InboxSDK on GitHub](https://github.com/InboxSDK/InboxSDK)
- [Issues](https://github.com/InboxSDK/InboxSDK/issues)
- [README](https://github.com/InboxSDK/InboxSDK/blob/main/README.md)
- [Releases](https://github.com/InboxSDK/InboxSDK/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/inboxsdk-inboxsdk
