Store data in the browser with localStorage and sessionStorage

Modern web applications frequently need to persist small pieces of data on the client side between page loads or between browser sessions without making a server request. The Web Storage API provides two mechanisms for this: localStorage, which persists data until explicitly cleared, and sessionStorage, which clears automatically when the browser tab is closed. Both are available in all modern browsers, require no library or polyfill, and hold data as key-value string pairs scoped to the current domain. This makes them significantly more appropriate than cookies for client-side state that does not need to be sent to the server on every HTTP request. Cookies were the original solution to client-side storage, but they carry overhead: every cookie is automatically appended to every HTTP request header for the matching domain, adding bandwidth cost to each request whether the server needs that data or not. For purely front-end state — a theme color preference the user has selected, whether they have dismissed a notification banner, the contents of a form they were filling in, the last tab they had active in a multi-tab interface, or whether they have already seen an onboarding popup — Web Storage is the correct tool. On WordPress sites specifically, common use cases include remembering whether a visitor has closed a cookie consent banner (avoiding repeated popup display), storing a partially completed multi-step form so data survives an accidental page refresh, caching the result of an AJAX request so subsequent visits do not require a server round trip, and saving user interface preferences like collapsed sidebar state or selected view mode in a WooCommerce product grid. The API surface is minimal: four methods plus a property. setItem(key, value) writes a value, getItem(key) reads it, removeItem(key) deletes it, and clear() removes everything. The length property reports how many items are stored. Values must be strings — objects and arrays need to be serialized with JSON.stringify() before writing and deserialized with JSON.parse() after reading. This pattern pairs well with the Fetch API to cache AJAX responses locally, and with debouncing to avoid excessive writes during rapid user interaction.

Problem: You need to persist small pieces of user state (preferences, dismissed notices, cached data) between page loads without server requests or cookies.

Solution: Use the Web Storage API directly in your theme’s JavaScript file:

// --- localStorage: persists until explicitly cleared ---

// Write a string value
localStorage.setItem( 'theme_color', 'dark' );

// Read it back
var color = localStorage.getItem( 'theme_color' ); // "dark"

// Store an object (must stringify)
var userPrefs = { sidebar: 'collapsed', viewMode: 'grid' };
localStorage.setItem( 'user_prefs', JSON.stringify( userPrefs ) );

// Read and parse the object
var stored = localStorage.getItem( 'user_prefs' );
var prefs  = stored ? JSON.parse( stored ) : {};
console.log( prefs.viewMode ); // "grid"

// Remove a specific key
localStorage.removeItem( 'theme_color' );

// --- sessionStorage: cleared when the tab is closed ---
sessionStorage.setItem( 'form_step', '2' );
var step = sessionStorage.getItem( 'form_step' );

// --- Practical example: dismiss a notice banner ---
var noticeBanner = document.getElementById( 'notice-banner' );
var dismissBtn   = document.getElementById( 'notice-dismiss' );

if ( localStorage.getItem( 'notice_dismissed' ) === '1' ) {
    noticeBanner.style.display = 'none';
}

if ( dismissBtn ) {
    dismissBtn.addEventListener( 'click', function() {
        localStorage.setItem( 'notice_dismissed', '1' );
        noticeBanner.style.display = 'none';
    } );
}

NOTE: localStorage has a storage limit of approximately 5MB per origin in most browsers — sufficient for preferences and small cached data but not for binary files or large datasets. Always wrap localStorage access in a try/catch block in production code: private browsing modes in some browsers disable Web Storage entirely and throw a SecurityError when any access is attempted. Also note that localStorage is synchronous and runs on the main thread — for large data volumes consider the asynchronous IndexedDB API instead to avoid blocking rendering. Do not store sensitive data like tokens, passwords, or personal information in localStorage — it is readable by any JavaScript running on the same origin, making it vulnerable to XSS attacks.