Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use window.matchMedia('(prefers-color-scheme: dark)').matches. It returns true when the page’s effective prefers-color-scheme media query currently matches dark. Subscribe to that query’s change event when the interface must react while it remains open.

The one-time JavaScript check

matchMedia() evaluates a CSS media-query expression and returns a MediaQueryList. Its synchronous matches property is the quickest way to branch application logic:

const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;

if (isDark) {
  console.log('The dark preference currently matches');
} else {
  console.log('The dark preference does not match');
}

A true value means the effective preference for this page matches dark. A false value means it does not match dark; it does not prove that the person explicitly selected light mode. The media feature also covers the case where no active preference has been expressed.

The query describes the page’s current browsing context rather than a guaranteed, device-wide setting. For example, an embedded document can receive the color scheme of its embedding context. The W3C describes the feature as reflecting the user’s desire that a page use a light or dark color theme in Media Queries Level 5.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Sale
Philips 24 Inch Computer Monitor FHD 100Hz VA VESA Flicker-Free, 241V8LB
  • CRISP CLARITY: This 23.8″ Philips V line monitor delivers crisp Full HD 1920x1080 visuals. Enjoy movies, shows and videos with remarkable detail
  • INCREDIBLE CONTRAST: The VA panel produces brighter whites and deeper blacks. You get true-to-life images and more gradients with 16.7 million colors
  • THE PERFECT VIEW: The 178/178 degree extra wide viewing angle prevents the shifting of colors when viewed from an offset angle, so you always get consistent colors
  • WORK SEAMLESSLY: This sleek monitor is virtually bezel-free on three sides, so the screen looks even bigger for the viewer. This minimalistic design also allows for seamless multi-monitor setups that enhance your workflow and boost productivity
  • A BETTER READING EXPERIENCE: For busy office workers, EasyRead mode provides a more paper-like experience for when viewing lengthy documents

Keep the page synchronized when the preference changes

Operating-system and browser theme settings can change while a tab is open. Keep one MediaQueryList object and listen for its change event:

const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)');

function applyColorScheme(isDark) {
  document.documentElement.dataset.theme = isDark ? 'dark' : 'light';
}

// Apply the current preference immediately.
applyColorScheme(darkModeQuery.matches);

// React if the effective preference changes later.
darkModeQuery.addEventListener('change', (event) => {
  applyColorScheme(event.matches);
});

The initial assignment is important: a change listener only runs for a later transition, so waiting for an event would leave the initial render unthemed. The event’s matches value is the new result and can be passed directly to your rendering or state logic. If code only needs a one-time branch, do not install a listener.

Clean up listeners in component lifecycles

In a component that can be mounted and destroyed, remove the callback during cleanup. Otherwise every remount can leave another callback attached to the same media query. A framework-agnostic pattern is:

const query = window.matchMedia('(prefers-color-scheme: dark)');
const onChange = (event) => applyColorScheme(event.matches);

query.addEventListener('change', onChange);

// Call this when the component is destroyed.
function cleanup() {
  query.removeEventListener('change', onChange);
}

For server-rendered applications, run this browser-dependent code only after the component reaches the browser. The window object does not exist during a server render; initialize a safe default there, then read matchMedia() after hydration to avoid a server/client markup mismatch.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use CSS instead when only appearance must change

If dark mode changes only colors, spacing, borders, or other presentation, JavaScript is unnecessary. CSS can respond directly to the same media feature:

Rank #2
Philips 22 Inch Computer Monitor FHD 100Hz VA VESA Flicker-Free, 221V8LB
  • CRISP CLARITY: This 22 inch class (21.5″ viewable) Philips V line monitor delivers crisp Full HD 1920x1080 visuals. Enjoy movies, shows and videos with remarkable detail
  • 100HZ FAST REFRESH RATE: 100Hz brings your favorite movies and video games to life. Stream, binge, and play effortlessly
  • SMOOTH ACTION WITH ADAPTIVE-SYNC: Adaptive-Sync technology ensures fluid action sequences and rapid response time. Every frame will be rendered smoothly with crystal clarity and without stutter
  • INCREDIBLE CONTRAST: The VA panel produces brighter whites and deeper blacks. You get true-to-life images and more gradients with 16.7 million colors
  • THE PERFECT VIEW: The 178/178 degree extra wide viewing angle prevents the shifting of colors when viewed from an offset angle, so you always get consistent colors
:root {
  color-scheme: light dark;
  --page-bg: white;
  --page-fg: #202124;
  --panel-bg: #f6f7f8;
}

@media (prefers-color-scheme: dark) {
  :root {
    --page-bg: #181a1b;
    --page-fg: #f1f3f4;
    --panel-bg: #242628;
  }
}

body {
  color: var(--page-fg);
  background: var(--page-bg);
}

.panel {
  background: var(--panel-bg);
}

This keeps the first style decision in the browser’s styling engine and avoids a flash caused by waiting for JavaScript. Add JavaScript only when the preference controls behavior: for example, choosing a chart palette, selecting an editor theme, persisting an application setting, or informing a component that cannot be expressed in CSS.

Declare supported schemes for browser-controlled UI

The color-scheme property in CSS, or an early document-head declaration, tells the browser which schemes the document supports:

<meta name='color-scheme' content='light dark'>

This allows browser-controlled interface elements, such as form controls and scrollbars where supported, to use a supported scheme and communicates the preference order. It does not generate your site’s color palette; your own CSS still needs to set colors. See the MDN documentation for the color-scheme meta element.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose the right implementation

Need Recommended approach Reason
Change visual styles only CSS @media (prefers-color-scheme: dark) No JavaScript branch or listener is required.
Run application logic based on the preference window.matchMedia() and .matches JavaScript receives a synchronous boolean for the current context.
React to a setting change while the page is open A change listener on the same MediaQueryList The event supplies the new matches value.
Make native browser controls follow supported schemes color-scheme: light dark or the matching meta element It declares support to the browser; it does not replace your palette.

A complete theme switcher example

The following example applies a data attribute immediately, updates it on changes, and lets CSS own the actual colors:

<!doctype html>
<html lang='en'>
<head>
  <meta charset='utf-8'>
  <meta name='color-scheme' content='light dark'>
  <style>
    :root { --bg: #fff; --fg: #202124; }
    :root[data-theme='dark'] { --bg: #181a1b; --fg: #f1f3f4; }
    body { margin: 0; background: var(--bg); color: var(--fg); }
  </style>
</head>
<body>
  <h1>Theme-aware page</h1>
  <script>
    const query = window.matchMedia('(prefers-color-scheme: dark)');
    const root = document.documentElement;

    function render(isDark) {
      root.dataset.theme = isDark ? 'dark' : 'light';
    }

    render(query.matches);
    query.addEventListener('change', (event) => render(event.matches));
  </script>
</body>
</html>

For a production application, keep the listener callback reference if the surrounding component has a teardown step, and make sure every component agrees on whether the data attribute represents a system preference or a user override. If users can explicitly choose a theme, treat that choice as a separate state: use matchMedia() for the system default, but do not overwrite an explicit selection every time the system changes.

Rank #3
Sale
Dell 24 Monitor - SE2426H - 23.8-inch FHD (1920x1080) 144Hz 1ms Display, in-Plane Switching (IPS) Technology, AMD FreeSync™, TÜV 3-Star 2X HDMI, Tilt
  • Clear visuals. Fluid motion: A 144Hz refresh rate and 1ms MPRT deliver smooth, tear‑free motion across work, gaming, and streaming for clearer, more fluid viewing.
  • Eye comfort: TÜV Rheinland 3‑star* certification reduces harmful blue light while preserving stunning color quality without compromise. *TÜV Rheinland 3-star eye comfort certification.
  • Wide viewing angle: Get consistent views across a wide 178° /178° viewing angle.
  • In-Plane Switching (IPS): See excellent color accuracy and consistency across wide viewing angles with In-plane Switching (IPS) technology.
  • Ultra-thin bezels: Maximize your viewing experience with thin bezels.

What the result means in embedded contexts

prefers-color-scheme is evaluated for the context in which the document is rendered. An iframe or embedded SVG may therefore use the embedding page’s effective scheme rather than exposing a universal reading of the host device. If an embedded widget looks unexpectedly light or dark, inspect the parent document and the embed’s own CSS before assuming that matchMedia() is malfunctioning. The preference semantics and context details are covered in MDN’s prefers-color-scheme reference.

Compatibility and practical support

MDN’s 2026 compatibility summaries mark the prefers-color-scheme media feature as widely available across browsers since January 2020. Window.matchMedia() is marked widely available since July 2015, and the MediaQueryList change event since September 2020. Those are broad browser milestones, not a guarantee for every embedded browser or webview, so test the actual environments your application supports:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The related Sec-CH-Prefers-Color-Scheme HTTP client hint and User Preferences API are experimental. They add server and deployment complexity and are not needed for ordinary client-side detection; use matchMedia() unless you have a specific, tested requirement for an experimental interface.

Testing dark-mode detection

  1. Load the page with the operating system or browser preference set to dark and verify that query.matches is true and the document receives the dark state.
  2. Switch the preference to light without closing the tab. Confirm that the listener runs once and receives false.
  3. Repeat the test with no explicit preference if the browser provides that state. Treat the result as “dark does not match,” not proof of an intentional light choice.
  4. Test an iframe or embedded SVG separately when your product uses embeds, because its effective context can differ from the top-level page.
  5. Check a server-rendered route with JavaScript disabled or delayed. CSS should still provide a usable palette when appearance is the only requirement.
  6. Inspect form controls and other browser-owned UI after adding color-scheme; the declaration should make supported controls harmonize with the page, while your custom components still need their own styles.

Troubleshooting common failures

“window is not defined”

The code ran during server rendering, a build step, or another non-browser environment. Move the call into browser-only lifecycle code or guard access to window; provide a deterministic server default and reconcile it after hydration.

The theme is always light

Check the exact query string, including prefers-color-scheme: dark. Then verify the browser or operating-system preference for the current profile. A false result also represents no active preference, and an embedded document may inherit its context from its parent.

Rank #4
Samsung 27" Essential S3 (S36GD) Series FHD 1800R Curved Computer Monitor
  • CURVED FOR ENHANCED ENGAGEMENT: An immersive viewing experience with a curved monitor that wraps more closely around your field of vision; It creates a wider view, enhancing depth perception and minimizing peripheral distraction
  • SMOOTH PERFORMANCE FOR SEAMLESS CONTENT: Stay in the action when playing games, watching videos, or working on creative projects; The 100Hz refresh rate reduces lag and motion blur so you don't miss a thing in fast-paced moments¹
  • MORE GAMING POWER: Gain the edge with optimizable game settings; Color and image contrast can be adjusted to see scenes more vividly and spot enemies hiding in the dark; Game Mode adjusts any game to fill the screen so you can view every detail²
  • KEEP IT EASY ON THE EYES: Care for your eyes and stay comfortable, even during long sessions; Advanced eye comfort technology certified by TÜV reduces eye strain by minimizing blue light and reducing irritating screen flicker²
  • INCREASED VERSATILITY: Connect to more; Plug devices straight into your monitor for increased flexibility, making your computing environment even more convenient

The page updates once but not after a system change

Make sure the listener is attached to the same MediaQueryList object whose matches value you read. If the component remounts, ensure cleanup does not remove a newly registered callback and that the callback updates the rendered state.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Only browser controls change, not the site

color-scheme declares supported browser UI schemes; it does not create your application palette. Add CSS variables and an @media rule, or apply a class/data attribute from JavaScript.

Dark mode flashes after load

Move presentation rules into CSS so the browser can evaluate them during initial style calculation. If JavaScript must set a theme attribute, run the smallest possible initialization before rendering visible content and keep the CSS fallback usable.

A test fails in a particular webview

Compatibility summaries describe broad browser support, not every embedded runtime. Test that webview’s version, confirm that it implements the change event, and retain a CSS-only path for purely visual adaptation.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

When you need screenshots of both light and dark states for visual regression or documentation, ScreenshotNeo can capture the page through one HTTP request. Its dark-mode option, device presets and custom viewport let you request the rendering context you need; you can also wait for a selector or network idle, inject CSS or JavaScript, hide selectors, and capture a full page or one CSS-selected element.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value
Sale
Sceptre New 22-Inch Gaming Monitor, FHD 1080p, Up to 144Hz, HDMI, DisplayPort, Built-in Speakers, Machine Black (E225W-FW144 Series, 2026)
  • 【INTEGRATED SPEAKERS】Whether you're at work or in the midst of an intense gaming session, our built-in speakers provide rich and seamless audio, all while keeping your desk clutter-free.
  • 【EASY ON THE EYES】 Protect your eyes and enhance your comfort with Blue-Light Shift technology. This feature reduces harmful blue light emissions from your screen, helping to alleviate eye strain during long hours of use and promoting healthier viewing habits.
  • 【WIDEN YOUR PERSPECTIVE】Our sleek minimal bezel design ensures undivided attention. The nearly bezel-free display seamlessly connects in a dual monitor arrangement, delivering an unobstructed view that lets you focus on more at once, completely distraction-free.

Use the API documentation at https://screenshotneo.com/docs/ for all parameters. A minimal cURL request is:

curl -G 'https://api.screenshotneo.com/v1/shot' -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp

The same request in Python:

import requests

r = requests.get(
    'https://api.screenshotneo.com/v1/shot',
    params={'access_key': 'YOUR_API_KEY', 'url': 'https://example.com'},
    timeout=90,
)
open('shot.webp', 'wb').write(r.content)

And in Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Cookie banners, newsletter popups and chat widgets are removed before the shot. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and each response reports the page verdict and billing status in X-Page-Verdict and X-Billed headers. ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

Summary decision

Use window.matchMedia('(prefers-color-scheme: dark)').matches for a current JavaScript decision, subscribe to change for live updates, and let CSS handle styling whenever possible. Declare color-scheme when browser-owned controls should follow the schemes your document supports, and remember that the result describes the effective page context rather than an infallible device-wide setting.

Frequently Asked Questions

Can a server reliably know a visitor’s dark-mode preference before JavaScript runs?

Not from this client-side API alone. The documented approach evaluates the preference in the browser; render a safe default on the server and reconcile it after the page reaches a browser context.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Should I store the value returned by matchMedia in localStorage?

Only if your application is deliberately persisting a user choice. A system preference can change, so a stored boolean should not silently replace the live media-query result unless your product defines that as an explicit override.

Does adding the color-scheme meta tag replace dark-mode CSS?

No. It declares schemes supported by the document so browser-controlled UI can adapt. Your page still needs CSS variables, media rules, or JavaScript for its own colors.

Why can an iframe report a different scheme from the top-level page?

The media feature is evaluated in the embedded context, and embedded documents can use the color scheme supplied by their parent. Test the embed as it is actually integrated.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.