Browser extensions

Build Playstack-connected browser extensions with MV3-safe runtime state, typed messaging, portable storage, and system-browser authentication.

Supported approaches

Extension authentication

available

Authenticate through the application's web flow and exchange a short-lived PKCE code without exposing passwords to extension UI.

Packages

@playstack/extension@playstack/client-auth

Frameworks and integrations

MV3 runtime lifecycle

available

Reconstruct service-worker state on wake and use alarms, storage, and retryable clients instead of process-lifetime assumptions.

Frameworks and integrations

Typed messaging and storage

available

Connect popup, options, content-script, and worker contexts through validated messages and explicit local or synchronized storage.

Frameworks and integrations

Extension policy and linting

available

Detect unsafe permissions, remote code, placement mistakes, and service-worker lifetime assumptions before review.

Frameworks and integrations

Frameworks and integrations

framework

React

Headless domain bindings plus optional Chakra application and admin compositions, with server authority kept outside the client.

framework

Next.js

Compose Playstack server contracts, React bindings, SSR handoff, and route adapters at the Next.js application edge.

Package reference

@playstack/extension

Build with the platform lifecycle

Manifest V3 service workers stop after inactivity. Any extension that keeps tokens, cached state, or initialization flags only in module memory will eventually fail outside development. Playstack makes reconstructible state and top-level event registration the default.

ts
import {
  createExtensionTokenStorage,
  createWebLockRefreshCoordinator,
  defineMessages,
  registerMessageHandlers,
} from '@playstack/extension'

// Access tokens live in storage.session, refresh tokens in storage.local;
// nothing durable lives in service-worker module memory.
export const tokens = createExtensionTokenStorage({ storage: browser.storage })
export const refreshes = createWebLockRefreshCoordinator({ locks: navigator.locks })

export const messages = defineMessages({
  'session.read': { request: sessionReadRequest, response: sessionReadResponse },
})

// Registered at the top level, so a restarted worker answers again.
registerMessageHandlers(browser.runtime, messages, {
  'session.read': async () => readSession(tokens),
})

Let WXT own the build

The package is a runtime layer, not another extension framework. WXT owns entrypoints, manifests, cross-browser output, and bundling. Playstack owns its application-facing authentication profile, typed messaging, storage conventions, API client, and React bindings.

Reuse application authentication

An extension opens the product authorization page with browser.identity.launchWebAuthFlow. The web application performs its normal login, MFA, passkey, or SSO policy and redirects back with a one-time code. The extension never renders or receives the user's password.

Implemented boundary

The extension and shared client-auth packages now provide authorization launch, split session/local credential storage, cross-context refresh coordination, validated messages and storage, optional permission requests, React session state, and MV3 interruption tests. The feature remains preview-level while it is exercised against a production WXT extension; WXT continues to own project generation and build output.

Go

Playstack Pro tag
OriginsPricingBlogNewsletterChangelogStatusRoadmap
ContributorsCommunityIn Use ShowcaseCase StudiesPartnersSponsors
FAQsSupportContact

© 2026 Playstack. All rights reserved.

With OSS
Terms of ServicePrivacy PolicyCookie PolicyImprint

By

Commune Software