Skip to main content

Overview

The TabsController is the central manager for all tab-related operations in Flow Browser. It handles tab creation, lifecycle management, activation, grouping, and persistence across windows and spaces.
Import: import { tabsController } from '@/controllers/tabs-controller'The controller is a singleton instance exported as tabsController.

Key Concepts

Tab Identity

Each tab has multiple identifiers:
  • id: Stable numeric ID that persists across sleep/wake cycles
  • uniqueId: Globally unique string identifier for persistence
  • profileId: The profile this tab belongs to
  • spaceId: The space this tab is currently in

Tab States

Visible

Tab is currently displayed in the window

Asleep

Tab’s WebContents destroyed to save memory (~20-50MB per tab)

Fullscreen

Tab is in fullscreen mode

Picture-in-Picture

Tab is floating as a PiP window

Tab Managers

Each tab has three associated managers:
  • TabLifecycleManager: Handles sleep/wake, fullscreen, and PiP
  • TabLayoutManager: Manages visibility, bounds, and positioning
  • TabBoundsController: Calculates and applies tab bounds

Core Methods

Tab Creation

createTab()

Creates a new tab with automatic profile and space resolution.
public async createTab(
  windowId?: number,
  profileId?: string,
  spaceId?: string,
  webContentsViewOptions?: Electron.WebContentsViewConstructorOptions,
  tabCreationOptions?: Partial<TabCreationOptions>
): Promise<Tab>
windowId
number
Window to create the tab in. Defaults to focused window.
profileId
string
Profile ID for the tab. Defaults to last used profile.
spaceId
string
Space ID for the tab. Defaults to last used space in profile.
webContentsViewOptions
WebContentsViewConstructorOptions
Electron WebContentsView options for customization.
tabCreationOptions
TabCreationOptions
Additional options:
  • url: Initial URL to load
  • asleep: Create tab in sleep mode
  • position: Tab position in stack
  • title, faviconURL: Restored state
  • navHistory, navHistoryIndex: Navigation history
return
Promise<Tab>
The newly created Tab instance
Example:
// Create a new tab with URL
const tab = await tabsController.createTab(
  undefined, // use focused window
  undefined, // use last profile
  undefined, // use last space
  undefined,
  { url: 'https://example.com' }
);

// Create a sleeping tab (for session restore)
const sleepingTab = await tabsController.createTab(
  windowId,
  profileId,
  spaceId,
  undefined,
  {
    asleep: true,
    title: 'Example Page',
    navHistory: savedHistory,
    navHistoryIndex: 2
  }
);

Tab Queries

getTabById()

Retrieve a tab by its numeric ID.
public getTabById(tabId: number): Tab | undefined

getTabByWebContents()

Find the tab associated with a WebContents instance.
public getTabByWebContents(webContents: WebContents): Tab | undefined

getTabsInSpace()

Get all tabs in a specific space across all windows.
public getTabsInSpace(spaceId: string): Tab[]

getTabsInWindow()

Get all tabs in a specific window across all spaces.
public getTabsInWindow(windowId: number): Tab[]

getTabsInWindowSpace()

Get tabs in a specific window and space combination.
public getTabsInWindowSpace(windowId: number, spaceId: string): Tab[]

Active Tab Management

setActiveTab()

Set the active tab or tab group for a space.
public setActiveTab(tabOrGroup: Tab | TabGroup): void
This method:
  • Updates the active tab map
  • Maintains activation history
  • Shows/hides tabs as needed
  • Updates focus
  • Emits active-tab-changed event
Example:
const tab = tabsController.getTabById(123);
if (tab) {
  tabsController.setActiveTab(tab);
}

getActiveTab()

Get the currently active tab or group for a window/space.
public getActiveTab(windowId: number, spaceId: string): Tab | TabGroup | undefined

isTabActive()

Check if a tab is currently active in its space.
public isTabActive(tab: Tab): boolean

Tab Groups

createTabGroup()

Create a new tab group (Glance or Split view).
public createTabGroup(
  mode: TabGroupMode,
  initialTabIds: [number, ...number[]],
  preferredGroupId?: string
): TabGroup
mode
'glance' | 'split'
required
  • glance: Stack tabs with quick switching
  • split: Display tabs side-by-side
initialTabIds
number[]
required
Array of at least 2 tab IDs to group together
preferredGroupId
string
Custom group ID (for session restore)
Example:
// Create a split view with two tabs
const tab1 = await tabsController.createTab();
const tab2 = await tabsController.createTab();

const splitGroup = tabsController.createTabGroup(
  'split',
  [tab1.id, tab2.id]
);

tabsController.setActiveTab(splitGroup);

getTabGroupById()

Retrieve a tab group by its string ID.
public getTabGroupById(groupId: string): TabGroup | undefined

getTabGroupByTabId()

Get the group containing a specific tab.
public getTabGroupByTabId(tabId: number): TabGroup | undefined

destroyTabGroup()

Destroy a tab group and ungroup its tabs.
public destroyTabGroup(groupId: string): void

Manager Access

getTabManagers()

Get all managers for a tab.
public getTabManagers(tabId: number): TabManagers | undefined
Returns an object with:
  • lifecycle: TabLifecycleManager
  • layout: TabLayoutManager
  • bounds: TabBoundsController
Example:
const managers = tabsController.getTabManagers(tabId);
if (managers) {
  // Put tab to sleep
  managers.lifecycle.putToSleep();
  
  // Wake tab up
  await managers.lifecycle.wakeUp();
  
  // Update layout
  managers.layout.updateLayout();
}

getLifecycleManager()

Get just the lifecycle manager for a tab.
public getLifecycleManager(tabId: number): TabLifecycleManager | undefined

getLayoutManager()

Get just the layout manager for a tab.
public getLayoutManager(tabId: number): TabLayoutManager | undefined

Events

The TabsController extends TypedEventEmitter with these events:
tab-created
[Tab]
Emitted when a new tab is created
tab-removed
[Tab]
Emitted when a tab is removed from the controller
active-tab-changed
[windowId: number, spaceId: string]
Emitted when the active tab changes in a window/space
current-space-changed
[windowId: number, spaceId: string]
Emitted when a window switches to a different space
Example:
tabsController.on('tab-created', (tab) => {
  console.log(`New tab created: ${tab.id} - ${tab.url}`);
});

tabsController.on('active-tab-changed', (windowId, spaceId) => {
  const activeTab = tabsController.getActiveTab(windowId, spaceId);
  console.log('Active tab changed:', activeTab);
});

Advanced Usage

Picture-in-Picture

// Disable PiP and return to tab
tabsController.disablePictureInPicture(tabId, true);

Space Management

// Set current space for a window
tabsController.setCurrentWindowSpace(windowId, spaceId);

// Get the current space for a window
const currentSpaceId = tabsController.windowActiveSpaceMap.get(windowId);

Position Normalization

Prevents tab position drift by resetting positions to sequential values.
// Normalize tab positions in a space
tabsController.normalizePositions(windowId, spaceId);

Page Bounds Updates

// Update all tab layouts when window bounds change
tabsController.handlePageBoundsChanged(windowId);

Data Structures

TabManagers

interface TabManagers {
  lifecycle: TabLifecycleManager;
  layout: TabLayoutManager;
  bounds: TabBoundsController;
}

TabCreationOptions

interface TabCreationOptions {
  uniqueId?: string;
  window: BrowserWindow;
  webContentsViewOptions?: Electron.WebContentsViewConstructorOptions;
  url?: string;
  asleep?: boolean;
  position?: number;
  title?: string;
  faviconURL?: string;
  navHistory?: NavigationEntry[];
  navHistoryIndex?: number;
}

Tab Class

Individual tab implementation

Windows Controller

Window management

Spaces Controller

Space management

Profiles Controller

Profile management

Build docs developers (and LLMs) love