How to Use localStorage with Playwright (Complete Tutorial with Examples)

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:

  1. Open the app in Chrome or Edge.
  2. Right-click anywhere on the page and choose Inspect (or press F12).
  3. Go to the Application tab.
  4. In the left sidebar, expand Local Storage under Storage.
  5. Click on the site URL.

If you see key-value pairs listed there, that confirms that the application is using localStorage.

Screenshot to demo how to Use localStorage with Playwright

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();
})

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%).

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:

🧠 Sharpen Your QA Mind With 5 Killer ChatGPT Prompts

Subscribe to receive 5 killer GPT Prompts to enhance your critical thinking and testing skills! 🎁

The prompts will be shared in your inbox within 5 minutes of subscription.

We don’t spam! Read our privacy policy for more info.

Thank you for reading this post, don't forget to subscribe!

Subscribe Newsletter!

We don’t spam! Read our privacy policy for more info.

Comments are closed