Connect+Documentation
ExtensionsAPI reference

connect.tabs

Read and control the profile's tabs — query, navigate, capture, message, and tab events.

Read and control the tabs in the extension's profile. Reading is gated by tabs.read; anything that changes a tab needs tabs.write. Messaging your own content script needs no capability.

  • Capabilities: tabs.read (query/inspect), tabs.write (open/close/navigate/…)
  • Namespace: connect.tabs

A tab object is { id, url, title, active, … }. "active" means the last-activated browsing tab in the profile — it survives the shell chrome taking focus.

Walkthrough

Find the active tab and act on it.

  1. Declare the capabilities you need — tabs.read to see tabs, tabs.write to change them:
    { "capabilities": ["tabs.read", "tabs.write"] }
  2. Find the active tab with query:
    const [tab] = await connect.tabs.query({ active: true });
  3. Act on it — navigate, reload, or message your content script.
  4. Stay in sync by subscribing to onActivated / onUpdated and re-rendering.

Methods

tabs.query(query?)

List tabs in the profile, optionally filtered.

How to use it

  1. Pass a filter (e.g. { active: true }) or nothing for all tabs.
  2. await the array; read tab.id / tab.url off each.

Signatureconnect.tabs.query(query?) · Capability: tabs.read

Parameters

ParameterTypeRequiredDescription
queryobjectnoFilter, e.g. { active: true }. Omit for all tabs.

ReturnsPromise<Tab[]>.

Examples

const [active] = await connect.tabs.query({ active: true });
const all = await connect.tabs.query();

tabs.get(tabId)

Fetch one tab by id.

How to use it

  1. Pass a known tabId.
  2. await it — wrap in try/catch, since an unknown id throws.

Signatureconnect.tabs.get(tabId) · Capability: tabs.read

Parameters

ParameterTypeRequiredDescription
tabIdnumberyesThe tab's id.

ReturnsPromise<Tab>. Throws if the id is unknown.

tabs.reload(tabId)

Reload a tab.

How to use it

  1. Pass the tabId to reload; await completion.

Signatureconnect.tabs.reload(tabId) · Capability: tabs.write · Returns Promise<void>.

tabs.navigate(tabId, url)

Navigate a tab to a URL.

How to use it

  1. Pass the tabId and a http(s)/about URL (other schemes are refused).
  2. await the navigation.

Signatureconnect.tabs.navigate(tabId, url) · Capability: tabs.write

Parameters

ParameterTypeRequiredDescription
tabIdnumberyesThe tab to navigate.
urlstringyesDestination. Refused for non-http(s)/about schemes.

ReturnsPromise<void>.

tabs.activate(tabId)

Focus (select) a tab.

How to use it

  1. Pass the tabId; await — it becomes the active tab.

Signatureconnect.tabs.activate(tabId) · Capability: tabs.write · Returns Promise<void>.

tabs.create(url)

Open a new tab.

How to use it

  1. Call create(url).
  2. Don't rely on the return for the id — await onCreated (or re-query) to get it.

Signatureconnect.tabs.create(url) · Capability: tabs.write

ReturnsPromise<{ requested: true }>. Resolves "requested": the new tab's id doesn't exist until it mounts, so re-query (or listen for onCreated) to get it.

await connect.tabs.create('https://example.com');
const off = connect.tabs.onCreated(({ id }) => { off(); /* got the new id */ });

tabs.close(tabId)

Close a tab.

How to use it

  1. Pass the tabId; await it closed.

Signatureconnect.tabs.close(tabId) · Capability: tabs.write · Returns Promise<void>.

tabs.move(tabId, index)

Reorder a tab in the strip.

How to use it

  1. Pass the tabId and a target index (clamped to the strip length).

Signatureconnect.tabs.move(tabId, index) · Capability: tabs.write

Parameters

ParameterTypeRequiredDescription
tabIdnumberyesThe tab to move.
indexnumberyesTarget position; clamped to the strip length.

ReturnsPromise<void>.

tabs.duplicate(tabId)

Open a copy of a tab (same URL).

How to use it

  1. Pass the tabId; like create, the new id arrives via onCreated, not the return.

Signatureconnect.tabs.duplicate(tabId) · Capability: tabs.write

ReturnsPromise<{ requested: true }>. Like create, resolves "requested" — the strip owns placement.

tabs.captureVisibleTab(tabId, options?)

Capture a tab's rendered page as an image.

How to use it

  1. Make sure you hold a host grant covering the tab's URL (plus tabs.read).
  2. Capture the visible tab; pass { format: 'jpeg', quality } for a smaller image.
  3. Handle a null result (unknown tab or empty frame).

Signatureconnect.tabs.captureVisibleTab(tabId, { format?, quality? }) · Capability: tabs.read plus a host grant covering the tab's URL

Parameters

ParameterTypeRequiredDescription
tabIdnumberyesThe tab to capture.
options.format'jpeg' | 'png'no'jpeg' (with quality) or PNG (default).
options.qualitynumberno0100, JPEG only.

ReturnsPromise<string | null> — a data URL, or null for an unknown tab or an empty frame.

Errors & edge cases

  • Needs a host grant matching the tab's URL — pixels are as sensitive as injection, so there is no ambient capture.
  • Capture reflects what's composited, so capture the visible tab; a backgrounded tab can come back empty (null).
const dataUrl = await connect.tabs.captureVisibleTab(active.id, { format: 'jpeg', quality: 80 });

tabs.sendMessage(tabId, data?)

Message your own content script in a tab.

How to use it

  1. In your content script, add connect.runtime.onMessage(...).
  2. From a surface, call sendMessage(tabId, data) and read { ok, reply }.

Signatureconnect.tabs.sendMessage(tabId, data?) · Capability: none

ReturnsPromise<{ ok, delivered, reply }>. No listener ⇒ ok: false.

const { ok, reply } = await connect.tabs.sendMessage(tab.id, { type: 'theme', value: 'dark' });

tabs.connect(tabId, options?)

Open a long-lived Port into your own content script in a tab — the reverse of a content script's runtime.connect. Use it when the background needs to push a stream into a page (live progress, a toggleable picker, settings sync) rather than fire one-shot messages.

How to use it

  1. In your content script, register connect.runtime.onConnect((port, sender) => …).
  2. From a surface (usually the background), await connect.tabs.connect(tab.id, { name }).
  3. Use the Port as usual; when the tab navigates or closes, both ends get onDisconnect.

Signatureconnect.tabs.connect(tabId, { name? }) · Capability: none (strictly intra-extension, like sendMessage — it only reaches your content script)

Parameters

ParameterTypeRequiredDescription
tabIdnumberyesThe tab hosting your content script.
options.namestringnoA label the CS reads as port.name.

ReturnsPromise<Port> (same shape as runtime.connect).

Errors & edge cases

  • Rejects when you have no injected content script with a runtime.onConnect listener at that tab's current URL — parallel to runtime.connect rejecting when nobody listens.
  • The port dies with the document: navigating the tab or closing it disconnects, and both ends hear onDisconnect — that's the point of a port over sendMessage.
// content script
connect.runtime.onConnect((port) => {
  if (port.name !== 'inspector') return;
  port.onMessage(({ highlight }) => setHighlight(highlight));
  port.onDisconnect(() => clearHighlight());
});

// background
const port = await connect.tabs.connect(tab.id, { name: 'inspector' });
port.postMessage({ highlight: true });

Events

All tab events need tabs.read and return an unsubscribe function.

tabs.onActivated(cb)

Fires when a different tab becomes active.

How to use it

  1. Subscribe; the callback gets the newly-active tab.
  2. Keep the returned function to unsubscribe later.

Payload — the newly-active tab.

const off = connect.tabs.onActivated((tab) => refresh(tab));
// later: off();

tabs.onUpdated(cb)

Fires on navigation / title changes (top frame).

How to use it

  1. Subscribe to re-read a tab whose URL/title changed.

Payload — the updated tab.

tabs.onCreated(cb)

Fires when a tab opens.

How to use it

  1. Use it to learn the id of a tab you just created / duplicated.

Payload{ id }.

tabs.onRemoved(cb)

Fires when a tab closes.

How to use it

  1. Subscribe to drop any per-tab state you were holding.

Payload{ id }.

Notes

  • Everything is profile-scoped — you only ever see and touch tabs in the profile the extension is installed into.
  • create/duplicate resolve before the tab's id exists; use onCreated or re-query when you need the id.
  • Tab groups are deliberately not implemented (tabs.group / tabGroups.* don't exist; feature-detect with 'group' in connect.tabs). Deferred to a future version.

On this page