Source Code

<div class="tp-wrap">
  <div class="tp-panel" id="tpPanel" data-theme="light">
    <div class="tp-panel-header">
      <span class="tp-panel-title">Preview Panel</span>
      <label class="tp-switch">
        <input type="checkbox" id="tpToggle" />
        <span class="tp-slider"></span>
      </label>
    </div>
    <p class="tp-panel-text">This panel's appearance follows the saved theme preference.</p>
    <div class="tp-status" id="tpStatus">No preference saved yet — using system default</div>
  </div>
</div>

Theme Preference Persistence Demo — Free HTML CSS JS Snippet

Theme Preference Persistence Demo · Misc · Plain HTML, CSS & JS · Live preview

What's included

Features

Theme choice saved via localStorage.setItem("theme", value) on every toggle
On load, localStorage.getItem("theme") is checked first and applied immediately if present
Falls back to prefers-color-scheme media query when no preference has been saved yet
Live status line explicitly showing whether the current theme came from storage or system default
data-theme attribute driven styling so the panel and any child element can theme off one attribute
Accessible toggle switch built from a real checkbox input with a styled slider
Fully self-contained vanilla JS with no external dependencies

About this UI Snippet

Theme Preference Persistence Demo — Toggle Saved to localStorage

Screenshot of the Theme Preference Persistence Demo snippet rendered live

This snippet demonstrates the standard pattern for persisting a user's light/dark theme choice across page loads using localStorage, with a visible status line so the persistence behavior is observable rather than implied.

Saving the preference

The toggle switch's change handler determines the new theme string ('dark' or 'light'), applies it to the preview panel via data-theme, and immediately calls localStorage.setItem('theme', theme) — the choice is durable the instant it's made, not just held in memory.

Restoring on load, with a fallback chain

On initialization, localStorage.getItem('theme') is read first. If it returns a valid saved value, that value is applied straight away and the status line reports it came from storage. If nothing has been saved yet (a fresh visitor), the code checks window.matchMedia('(prefers-color-scheme: dark)').matches and falls back to the OS-level preference instead — so the panel never defaults to an arbitrary hardcoded theme when the user hasn't made an explicit choice.

Making persistence visible

A small monospace status line below the panel reads either "Saved to localStorage: dark" (once an explicit choice has been stored) or "No preference saved yet — using system default" (before any choice is made), and updates live on every toggle — turning what would otherwise be an invisible background behavior into something a reader can directly observe working.

Step by step

How to Use

  1. 1
    Load the snippetClick "Theme Preference Persistence Demo" in the sidebar to load its HTML, CSS, and JS into the editor panels. The preview updates instantly.
  2. 2
    Edit the codeModify any panel — HTML, CSS, or JS. The preview refreshes as you type. Use Reset in each panel header to restore the original.
  3. 3
    Preview on devicesClick the Mobile (375px), Tablet (768px), or Desktop buttons in the preview header to check responsiveness.
  4. 4
    Export in your formatClick "HTML" to download a standalone file, "JSX" for a React component, "Tailwind" for a React + Tailwind CSS component, "Tailwind HTML" for a standalone HTML file with Tailwind CDN, "Vue" for a Vue 3 SFC with <template>/<script setup>/<style scoped>, or "Angular" for a standalone Angular .component.ts file. "Copy all" copies the full code to clipboard.
  5. 5
    Save your versionClick "Save as", type a name, and press Enter. Your snippet saves to IndexedDB and appears in the Saved tab.

Real-world uses

Common Use Cases

App-wide dark mode toggles
The exact persistence pattern used by real dashboards and web apps for remembering theme choice.
Reference for localStorage plus prefers-color-scheme
Shows the correct precedence order: explicit user choice first, OS preference as fallback.
Teaching persistence patterns
A clear, minimal example of read-on-load, write-on-change localStorage usage.
Settings panels
Drop directly into an app settings page as the appearance/theme control.

Got questions?

Frequently Asked Questions

localStorage.getItem("theme") returns null, so the code checks window.matchMedia("(prefers-color-scheme: dark)") and applies dark or light based on the operating system's setting, without writing anything to storage yet.

Only when the toggle is switched. Until the user makes an explicit choice, the theme is derived live from the system preference each load; the moment they toggle it, that choice is written via localStorage.setItem and takes precedence on every future load.

init() and the change handler both call updateStatus with a boolean flag indicating whether the value came from localStorage.getItem versus the prefers-color-scheme fallback, and the status text branches on that flag.

Yes — apply the same data-theme attribute to document.documentElement or document.body instead of (or in addition to) the panel element, and define your dark-mode CSS rules against that scope.