Animated Dark Mode Toggle for Framer, Webflow and React (Free)
A dark mode toggle sounds like an afternoon's work, and then the details show up. The page flashes white before the dark theme loads. The choice isn't remembered. The toggle ignores what the visitor's device says. And on Framer, there is no built-in way for visitors to choose at all.
This post walks through what a good theme switch needs, and how to add one to Framer, Webflow, Next.js, or plain HTML using Theme Switch, which is free.
What a good theme switch does
- No flash. The theme has to be set before the first paint. A script that loads with the rest of the page is too late, so a visitor who picked dark sees a light flash on every page load.
- Three states. System, Light and Dark. Choosing "System" should keep following the device as it changes.
- Remembers the choice, and keeps other tabs and other switches on the page in sync.
- Works with your CSS. Tailwind's class strategy,
data-themeselectors, orprefers-color-schemerules. - Accessible. Real controls, keyboard support, a visible focus ring, and no fade for Reduce Motion.
Why Framer needs this
Framer's color styles have Light and Dark values, but they switch only when the visitor's device is in dark mode. Theme Switch lets the visitor choose, and every fill, text color, and border that uses a color style follows. It does this by rewriting the page's prefers-color-scheme rules while a theme is picked.
How it works
It sets data-theme and a dark class on <html>, plus color-scheme. A small head snippet (1,020 bytes, no requests) sets the theme before the page paints. The switch itself is a script build of 2.95 KB gzipped with no dependencies. Where the browser supports the View Transitions API, themes cross-fade; elsewhere there's no animation.
There are three styles, set with the variant attribute:
segmented: System / Light / Dark side by side (the default)icon: one button that cyclesswitch: light and dark only
Sizes are sm, md, lg (28, 34 or 42 px tall). The look is neutral or copper, and accent takes any CSS color.
Plain HTML
Paste the contents of head-snippet.html from the download as high in <head> as you can, then add your dark colors, the switch, and the script:
<head>
<!-- 1. first thing in <head>: paste head-snippet.html here -->
<style>
:root { --bg: #fff; --text: #1c1917; }
html[data-theme="dark"] { --bg: #0f0e0d; --text: #f5f5f4; }
body { background: var(--bg); color: var(--text); }
</style>
</head>
<body>
<!-- 2. the switch, anywhere -->
<pf-theme-switch></pf-theme-switch>
<pf-theme-switch variant="icon" look="copper"></pf-theme-switch>
<!-- 3. the script, once (async or defer is fine) -->
<script src="/js/theme-switch.min.js" defer></script>
</body>
Your own buttons work too: add data-theme-set="dark" (or light, system) to any button, or data-theme-toggle to toggle.
Framer
- Site Settings → General → Custom Code → Start of
<head>tag: paste the head snippet. - Assets → Code → New code file named
ThemeSwitch, and pasteframer/ThemeSwitch.tsxfrom the download. - Drag Theme Switch onto your nav and pick the style, size, look and accent in the properties panel. Leave Storage Key, Attribute and Default as they are.
- Assets → Colors: give each color style a Dark value. Use color styles everywhere, publish, and switch on the live site.
Know the limits. Per the README, these don't follow the switch: colors typed in as plain hex values instead of color styles, component variants, the Framer editor canvas, and embeds or iframes such as YouTube and maps. Framer has no dark image variants either, though two images plus a line of Custom Code CSS handle logos; the README shows how.
Webflow
- Site settings → Custom code → Head code: paste
head-snippet.html. - Footer code: paste
script/footer-code.html(nothing to host). - Add an Embed element where the switch goes:
<pf-theme-switch></pf-theme-switch>. - Style dark mode with a custom CSS embed, for example:
html[data-theme="dark"] body { background: #0f0e0d; color: #f5f5f4; }
- Publish. Custom code runs on the published site, not in the Designer, and needs a paid site plan.
React and Next.js
Copy ThemeSwitch.tsx into your project. It exports <ThemeSwitch>, <ThemeScript> and a useTheme() hook:
// app/layout.tsx (Next.js): the snippet, before the page paints
import { ThemeScript, ThemeSwitch, useTheme } from "./ThemeSwitch"
<html lang="en" suppressHydrationWarning>
<head><ThemeScript /></head>
<body>{children}</body>
</html>
// anywhere
<ThemeSwitch variant="icon" look="copper" />
const { mode, theme, setMode, toggle } = useTheme()
For Tailwind, set darkMode: "class" (v3) or add @custom-variant dark (&:where(.dark, .dark *)); (v4). Theme Switch also uses the same localStorage key and values as next-themes, so visitors keep their choice if you switch libraries.
See it in context
Theme Switch is listed with the other Parallel Studio effects on /effects, and the Theme Switch page has a live demo and the download.
Pricing
Theme Switch is free, for unlimited personal and commercial projects including client sites. You can't sell or give away the component itself. Download it from the Theme Switch page; it opens a free checkout with no payment.
If you later want the paid effects too, the All-Access Pass is $49.
Whichever way you add dark mode, the test is the same: pick Dark, reload the page, and watch for a white flash.