Skip to content

Platform in Platforms

Firefox taskbar web apps on Windows

Published Updated

Firefox 143 on Windows can pin a site to the taskbar and run it in a simplified window, a feature Mozilla ships under the name web apps and calls taskbar tabs in its own source. The pin is made from the address bar rather than from the site, and the window it opens keeps the profile and the container it was created with.

The feature is one browser component, documented in the Firefox source tree as Web Apps in Firefox. That page states the feature is enabled by the preference browser.taskbarTabs.enabled, and describes its entry point as a button on the URL bar rather than a manifest member or a page API. The architecture document assigns that button to a module it calls the page action, which shows and hides itself depending on whether the page has an HTTP or HTTPS scheme.

The component documentation gives both halves of the flow. Clicking the “Add tab to taskbar” button on the URL bar creates a new window and prompts the user to pin it to the taskbar. Clicking “Remove tab from taskbar” turns that window back into a normal tab and, if that was the last window, unpins it from the taskbar. Firefox keeps information about each web app in taskbartabs/taskbartabs.json inside the user’s profile, a file the same page marks as intended for use internally by Firefox.

The icon is chosen the first time a web app is created, by iterating through sources (manifests, favicons) until a valid icon is found, and an existing web app saves its icon on the filesystem and loads it when needed. So the manifest and the favicon are where a site can influence what the taskbar shows.

A pinned window is still the same browser. The “External Interactions” section of Web Apps in Firefox records the consequences that matter when you test an app there:

  • Web apps are tied to the profile they are created with, so web app pages have the same access, controls and add-ons as a normal tab. The Firefox 143.0 release notes (September 16, 2025) describe this as pinning sites that run “without losing access to your installed add-ons”.
  • Web apps are tied to the container, called a contextual identity in the source, they are created with, and launching via the taskbar reopens them in that container. The documentation presents this as a way for users to isolate sites without requiring a new profile per web app.
  • Web app windows are not tied to the lifetime of normal browser session restore. They are not restored for users who have enabled “Open previous windows and tabs”, and they do not prevent the restoration of windows that session restore does handle.

Clicking the taskbar shortcut starts Firefox with a command line, which the architecture document gives as:

firefox -taskbar-tab <id> -new-window <start url> -profile <profile path> -container <usercontextid>

The same document records what happens when the referenced web app is absent: a new one is created, though not with that ID. When the web app exists its start URL is used, and otherwise the start URL from the shortcut opens instead, as a fallback.

The component documentation lists web apps as available on Windows, both MSIX and non-MSIX, enabled by default, and on Linux, unsandboxed and under Flatpak, disabled by default. It states that macOS support is not available and gives the reason: macOS assumes that two different visible applications will be in two different processes, while Firefox web apps are in the same profile and process as the rest of the browser, so the Dock is not able to distinguish them. The architecture document adds that Linux under Snap is not supported.

The Firefox 143.0 release notes scope the shipped feature more narrowly than the component does. They describe it as a Windows feature and exclude Firefox installs from the Microsoft Store. Read the release notes for what Firefox 143 does, and the component documentation for where the component has reached since.

A site cannot create the pin, so the code worth writing is the code that makes the simplified window a reasonable place to land.

Reacting when a window stops being a web app

Section titled “Reacting when a window stops being a web app”

The architecture document puts “ejecting” web apps back into the tabs they came from in TaskbarTabsWindowManager, so one window can change from an app surface back to a tab while the page is loaded. A layout that keys on the display-mode media feature should therefore listen rather than read once.

The mode a Firefox taskbar web app reports is minimal-ui, not standalone: TaskbarTabsWindowManager.sys.mjs sets browser.browsingContext.displayMode = "minimal-ui"; on each tab of the window it opens, and sets it to "browser" when the app is ejected. A check written against standalone alone therefore reports a browser surface inside the window it meant to detect, so test the installed modes as a set.

// Firefox taskbar web apps report minimal-ui. Other installed display modes depend
// on the mode requested by the manifest and browser support.
const APP_MODES = ['minimal-ui', 'standalone', 'window-controls-overlay'];
function trackAppSurface(onChange) {
if (!('matchMedia' in window)) {
// No matchMedia: render the browser layout and stop here.
onChange('browser');
return () => {};
}
const queries = APP_MODES.map((mode) => window.matchMedia(`(display-mode: ${mode})`));
const apply = () => onChange(queries.some((query) => query.matches) ? 'app' : 'browser');
apply();
queries.forEach((query) => query.addEventListener('change', apply));
return () => queries.forEach((query) => query.removeEventListener('change', apply));
}
const stop = trackAppSurface((surface) => {
document.documentElement.dataset.surface = surface;
});

Read the individual queries instead of the some() result when the distinction matters to the layout: minimal-ui is the Firefox taskbar window, while the effective mode elsewhere depends on the mode requested by the manifest and browser support. Keep the returned teardown function, because a single-page app that re-runs this on every route change otherwise stacks one listener per navigation.

Serving the icon and name the pin will use

Section titled “Serving the icon and name the pin will use”

Firefox uses the manifest’s name, start URL, scope, and icons. When no manifest icon is available, it also tries a favicon.

{
"name": "Example Reader",
"short_name": "Reader",
"start_url": "/app/",
"scope": "/app/",
"display": "standalone",
"icons": [
{ "src": "/icons/app-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/app-256.png", "sizes": "256x256", "type": "image/png" },
{ "src": "/icons/app-512.png", "sizes": "512x512", "type": "image/png" }
]
}

name, not short_name, is the string the registry entry carries: TaskbarTabsRegistry.sys.mjs creates it with name: manifest.name ?? generateName(aUrl), deriving a name from the URL when the manifest has none. Keep short_name for the platforms that use it, and write a name short enough to read under an icon.

The icon pass asks for 256 px: findBestManifestIcon in TaskbarTabs.sys.mjs discards icons whose purpose does not include any, sorts the rest by width, returns the first candidate whose size is at least 256, and falls back to the largest candidate when that search finds none. The same function substitutes Infinity for an empty or any sizes value, so such an entry sorts above every pixel size. In the manifest above the 256 px icon is the one that search reaches, so serve a real asset at that size rather than relying on a small favicon.

The preference that gates the feature is set per platform in Firefox’s own default preference file, which is where the per-platform defaults above come from.

The address-bar entry point is drawn by the page action module, so a user whose build reads the #else default has no button to click until the preference is changed. That is the mechanism behind the Linux “disabled by default” line in the component documentation.

Specifications

SpecificationStatus
None.