If you’re working with Playwright in 2025 and still haven’t explored how to use use localStorage with Playwright, or don’t even know why it matters, you’re seriously missing out a lot.
But before we deep dive into how to use localStorage
with Playwright, let’s take a step back. First, it’s important to understand what localStorage
actually does, a few real-world use cases, and how you can figure out if your application is even using it in the first place. [You can skip the introduction and go directly to the code snippets if preferred]
What are the real-world use cases of localStorage?
localStorage
is used to store data across browser sessions. In 2025, a lot of websites let you choose your preferred theme like dark, light, or system-based. Once you make a selection, that choice is typically saved in localStorage
. Unless you manually clear it, the site will remember your theme for the next visits.
Any such data which is not so critical and only required to load certain state of application are generally avoided for server-side storage. Such data are stored at client side, preferably in localStorage.
localStorage commonly used for things like:
- Simple user settings (like grid/list view, filters, tab selections)
- Theme preferences (e.g., dark mode vs. light mode)
- Language or locale settings
- Onboarding checklists (so the same tips don’t show again)
- Feature flags or experimental UI toggles
- Storing JWT Tokens
- Recently visited items on e-commerce site
- Recently read articles on the news site
- Capturing analytics data
- Enabling some offline capabilities
How do I check whether the application uses localStorage
?
To check if an application is using localStorage
, you can inspect it directly in your browser:
- Open the app in Chrome or Edge.
- Right-click anywhere on the page and choose Inspect (or press
F12
). - Go to the Application tab.
- In the left sidebar, expand Local Storage under Storage.
- Click on the site URL.
If you see key-value pairs listed there, that confirms that the application is using localStorage
.

How does Playwright Automation get benefit from localStorage?
localStorage helps application to understand the user preference.
For example, an e-commerce website may utilise localStorage to store the recently visited items. Based on this information, they may recommend additional items for your purchase in next visit.
When testing Recommendation UI, automated tests often need to manipulate localStorage
to simulate prior user behavior (like past purchases or views) that triggers specific recommendations. But in automated tests, placing an order just to populate localStorage
would be slow, flaky, and hard to control. Directly manipulating the localStorage by the test script is better approach in such case.
If we talk about the benefits of localStorage manipulation in this particual case:
- It saved the time to place order to only populate the values in localStorage.
- Flakiness is reduced significantly as there are less UI steps involved in the automation.
- You have better control on the test data as you can use variety of items which might be difficult to order in lower environment.
- It allows you to mimic the scenario where user has not placed any order so far. Test script can directly clean the localStorage.
- Negative testing can be easily done by ading junk values to localStorage.
localStorage with Playwright – Code Snippets
Example #1 – When localStorage needs to set using shared browserContext
Below code snippet demonstrates the fundamental approach to set the localStorage as part of beforeAll hook using browserContext. This approach is recommened only when a sharedState is required to run all the tests from a file sequentially. Otherwise, this approach should be avoided as it is not parallel safe.
import { test, expect, Page } from '@playwright/test';
let page: Page;
test.beforeAll(async({browser}) => {
const browserContext = await browser.newContext({
storageState: {
cookies: [],
origins: [
{
origin: 'https://playwright.dev/',
localStorage: [
{
name: 'localStorageKey',
value: 'localStorageValue'
}
]
}
]
}
});
page = await browserContext.newPage()
})
test.afterAll(async() => {
await page.close()
})
test('Local storage test', async () => {
await page.goto('https://playwright.dev/');
await page.getByRole('link', { name: 'Get started' }).click();
await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
// get localstorage
const storage = await page.evaluate(() => JSON.stringify(localStorage));
console.log(storage);
})
Example #2 – Setting localStorage using the browserContext at test level
Here, the localStorage values are set by a browserContext intialised at test level. This goes well with parallel execution as well, as every test will have its own browser context.
test('Local storage test', async ({browser}) => {
const browserContext = await browser.newContext({
storageState: {
cookies: [],
origins: [
{
origin: 'https://playwright.dev/',
localStorage: [
{
name: 'localStorageKey',
value: 'localStorageValue'
}
]
}
]
}
});
let page = await browserContext.newPage()
await page.goto('https://playwright.dev/');
await page.getByRole('link', { name: 'Get started' }).click();
await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
// get localstorage
const storage = await page.evaluate(() => JSON.stringify(localStorage));
console.log(storage);
})
Example #3 – Injecting localStorage from a File (Suitable for different personas, environments)
A cleaner version for larger projects. Here, the values are stored in localstorage-{env}.json
and directly dumped to browser using test.use({})
.
import { test, expect, Page } from '@playwright/test';
test.use({ storageState: '.localstorage-dev.json' });
test('Local storage test using file', async ({browser}) => {
const browserContext = await browser.newContext();
let page = await browserContext.newPage()
await page.goto('https://playwright.dev/');
await page.getByRole('link', { name: 'Get started' }).click();
await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
// get localstorage
const storage = await page.evaluate(() => JSON.stringify(localStorage));
console.log(storage);
})
.localstorage-dev.json
{
"cookies": [],
"origins": [
{
"origin": "https://playwright.dev",
"localStorage": [
{
"name": "userRole",
"value": "admin"
},
{
"name": "localStorageKey",
"value": "localStorageValue"
}
]
}
]
}
Example #4 – Injecting localStorage values at run time at test level using Vanilla JS
A faster approach with more control as the values can be customised at test level. This approach is best suitable when you are testing various features and each of them are controlled by different values of localStorage.
test('LocalStorage (Vanilla JS)', async () => {
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://playwright.dev/');
// Set localStorage
await page.evaluate(() => {
localStorage.setItem('role', 'admin');
localStorage.setItem('auth', 'true');
});
// Validate localStorage value
const role = await page.evaluate(() => localStorage.getItem('role'));
console.log('role:', role); // should log: admin
expect(role).toBe('admin');
await browser.close();
});
Example #5 – Storing localStorage values in a file
This is another useful interface given by Playwright to save the state in a file for reusability. In this case, the storage state along with cookies, sessionStorage and localStorage dumped into a file localstorage-prod.json to make reusable.
test('Save Local storage test', async () => {
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://playwright.dev/');
// Set localStorage manually
await page.evaluate(() => {
localStorage.setItem('localStorageKey', 'localStorageValue');
localStorage.setItem('userRole', 'admin');
});
// Save the state
await context.storageState({ path: 'localStorage-prod.json' });
await browser.close();
})
How I Saved Time on 150 Playwright Tests with storageState
We had a suite of 150 Playwright tests that each went through a full login flow because we reset the test data before every test class run. Sometimes, 2-3 times in a test class as well.
That means, every time we needed to login and set certain values in localStorage, adding 5-7 seconds of delay in every describe block of our tests. Additionally, our tests run sequentially so these 5-7 seconds sum up to a significantly slower execution.
To eliminate this overhead, we switched to using Playwright’s storageState
with preloaded localStorage
and cookies, allowing each test to start in an authenticated state without UI login. This change significantly cut test runtime, removed all login flakiness with a better execution time (reduced by ~ 7-8%).
Always Keep at least one End-to-End Test per Feature
It is uttermost important that we don’t eliminate the end-to-end flow completely while using the storage state or localStorage with playwright. For every feature, we must keep at least one end-to-end test that goes through the actual login and interaction process without any shortcuts.
This ensures the application is truly setting and using localStorage
as intended, not just relying on test scaffolding.
Further Reading:
- Authentication | Playwright
- GetByRole in Playwright
- Why you should not carry selenium habits to Playwright
Thank you for reading this post, don't forget to subscribe!
Comments are closed