Playwright Authentication: Reusing Logged-In State in 2026
A step-by-step guide on persisting browser storage state to speed up tests by avoiding logging in for every test file.
playwright-v1-49-matrix
Playwright Authentication: Reusing Logged-In State in 2026
Logging in for every test file degrades test speed and strains servers. By writing the browser state to a JSON file once and loading it for each test, we bypass repetitive login flows. This guide outlines how to configure state caching.
Introduction
End-to-end test execution times scale quickly with the size of your test suite. If every test file executes a full authentication flow, your CI pipeline slows down. Storing authentication states locally solves this problem.
Playwright permits caching cookies and localStorage from a successful login session. Subsequent browser contexts load these values immediately, bypassing login pages.
How State Caching Works
When you authenticate on a website, the server returns authentication tokens or cookies. Playwright captures these parameters and serializes them into a local JSON file.
This cached file is called the storage state. Any new browser context can boot up pre-populated with these credentials.
graph TD
LoginTest["Setup: Perform Login UI Flow"] --> SaveState["Save State: page.context().storageState()"]
SaveState --> JSONFile["Credentials JSON File (state.json)"]
JSONFile --> LoadState["Runner Config: storageState: 'state.json'"]
LoadState --> Test1["Execute Test Suite 1 (Authenticated)"]
LoadState --> Test2["Execute Test Suite 2 (Authenticated)"]Authentication Architecture
The sequence of storing and loading authentication states follows a clean lifecycle:
sequenceDiagram
participant Config as Playwright Config
participant GlobalSetup as Setup File
participant AppServer as App Auth Server
participant TestSuite as Authenticated Tests
Config->>GlobalSetup: Trigger Setup Project
GlobalSetup->>AppServer: Send Login Credentials
AppServer-->>GlobalSetup: Return Session Cookies & Tokens
GlobalSetup->>GlobalSetup: Write context to state.json
Config->>TestSuite: Inject state.json into Context
TestSuite->>AppServer: Bypass Login page, send cookies
AppServer-->>TestSuite: Render Protected DashboardImplementation Steps
Follow this implementation to establish state caching in your project.
1. Configure Playwright Setup Project
Update your playwright.config.ts to include a setup project that runs before other tests:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
// Define global path for storage state output
use: {
baseURL: 'http://localhost:3000',
},
projects: [
// 1. Setup project to run first
{
name: 'setup',
testMatch: /global\.setup\.ts/,
},
// 2. Main testing project dependent on setup
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
// Feed the saved credentials file into tests
storageState: 'playwright/.auth/user.json',
},
dependencies: ['setup'],
},
],
});2. Implement the Global Setup Test
Write the authentication routine in tests/global.setup.ts to capture the state:
import { test as setup, expect } from '@playwright/test';
const authFile = 'playwright/.auth/user.json';
setup('authenticate user', async ({ page }) => {
// Navigate to login endpoint
await page.goto('/login');
// Fill credentials fields
await page.getByLabel('Email Address').fill('testuser@playwrightpad.info');
await page.getByLabel('Password').fill('SecurePassword123');
// Submit and verify authentication completion
await page.getByRole('button', { name: 'Sign In' }).click();
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
// Save storage context containing cookies and tokens
await page.context().storageState({ path: authFile });
});3. Writing Authenticated Tests
With the state loaded globally, standard tests start already logged in:
import { test, expect } from '@playwright/test';
test('should render secure user profile settings page', async ({ page }) => {
// Navigate directly to protected route
await page.goto('/settings/profile');
// Assert user is logged in automatically
await expect(page.getByText('Logged in as testuser')).toBeVisible();
});Performance Benchmarks
The table below outlines test suite execution times when caching authentication versus logging in for every test file.
| Metric | Repeated UI Login | Cached Storage State |
|---|---|---|
| Average Login Duration | 4.2 seconds | 0.0 seconds |
| Total Pipeline Time (10 tests) | 42.0 seconds | 0.8 seconds (single setup) |
| Server Load (Request Volume) | High (hits OAuth server constantly) | Low (reads cookie array from file) |
| Test Flakiness Rate | High (UI flows can fail randomly) | Low (direct token validation) |
| Database Writing Overhead | High (session tables update) | Minimal (session reused) |
Best Practices for Session State
.auth/ directory containing credentials to your .gitignore to prevent committing session tokens to remote repositories.Here are a few key practices:
.auth/ folder during pipeline builds to guarantee fresh tokens.admin.json, user.json).Common Mistakes to Avoid
| Bad Pattern | Recommended Alternative |
Committing playwright/.auth/ to Git | Add the directory path to your .gitignore file |
| Using state for login form checks | Run separate tests without loading saved JSON state |
| Exposing tokens in setup logs | Redact logs and run setups quietly |
Frequently Asked Questions
Why does my authentication state fail on CI pipelines?
Session tokens might expire or be tied to IP addresses. Ensure setup runs on CI or adjust token expiration dates on dev environments.
How do I store state for multiple user roles?
Define separate setup projects in playwright.config.ts, exporting states like admin.json and editor.json. Overwrite context values per test.
What files does storageState cache?
It extracts and writes all active HTTP cookies, sessionStorage arrays, and localStorage key-value matrices to a flat JSON file.
How do I verify if my storage state is loaded?
Inspect the browser cookies using dev tools in headed mode, or check network headers for authentication parameters.
Can I manually modify the saved JSON state?
Yes. It is a flat JSON file. You can read, modify, or inject additional cookies using Node.js filesystem modules prior to running tests.
Does this method support multi-factor authentication (MFA)?
For MFA, mock or disable authentication checks on staging, or use token generators to bypass interactive screens during setup.
How often should I regenerate the state file?
Regenerate it once per test execution run. The configuration setup dependency handles this automatically.
What is the default path to save setup states?
Store them inside a directory like playwright/.auth/ to keep project workspaces clean.
Should I delete the state file after tests complete?
It is not required, though cleaning up cache directories during CI runs ensures pipeline integrity.
Does cached state work with Firefox and WebKit?
Yes. Saved state JSON structures are browser-agnostic and load successfully across all engines.
Summary
Implementing storage state caching accelerates test execution and protects servers from login traffic. Saving tokens once simplifies pipeline management.
Related Articles
About The Author
PlaywrightPad Editorial reports on Chromium engines, E2E test optimizations, and AI integration specifications.
Newsletter
Get weekly browser reports sent directly to your inbox.