Skip to content
Template Empire / components

Search components

10 results

Browse components

System Segment

A free segmented theme switcher for React and Next.js: light, system and dark in one dark mode switch.

  • Automated axe check
  • Reduced-motion tested
  • SSR build verified
  • Motion
  • 2 runtime packages

System Segment is a free dark mode switch for React and Next.js, built with Motion and styled with Tailwind. It is a three-option segmented theme switcher: light, system and dark sit in fixed equal-width segments, and a spring-loaded pill thumb slides to whichever one you select while the icons crossfade from 60 to 100 percent opacity. Each option pairs a lucide icon with a visible text label, so the control is never icon-only.

The component is fully controlled: you own the value and it calls onChange with the selection. It ships proper radio-group semantics with roving tabindex and arrow-key navigation, and it treats system as just another value, so resolving the OS preference and persisting the choice stay in your app. With reduced motion enabled the thumb jumps into place instantly and icon states swap with no crossfade.

Appearance

Following the system (dark in this demo).

This panel carries its own scoped theme. The control is fully controlled and treats system as just another value, so your app decides how to resolve and persist it.

Install

Add the @te registry to your components.json once:

{
  "registries": {
    "@te": "https://templateempire.io/r/{name}.json"
  }
}

Then install the component:

npx shadcn@latest add @te/theme-segment

Usage

"use client";

import { useState } from "react";
import { ThemeSegment, type ThemeSegmentValue } from "@/components/theme-segment";

export default function Page() {
  const [theme, setTheme] = useState<ThemeSegmentValue>("system");

  return <ThemeSegment value={theme} onChange={setTheme} />;
}

Props

PropTypeDefaultDescription
value"light" | "system" | "dark"requiredControlled current selection.
onChange(value: "light" | "system" | "dark") => voidrequiredCalled with the selected value on click or arrow-key selection.
size"sm" | "md""md"Control density.
classNamestringundefinedAdditional classes merged onto the radiogroup.

Customisation

System Segment declares no tokens of its own. The track and thumb paint with the global background, foreground, border and ring semantic tokens, so retheming those tokens rethemes the control with the rest of your page. Switch density with the size prop and shape or space the group from the className prop:

<ThemeSegment
  value={theme}
  onChange={setTheme}
  size="sm"
  className="mt-4"
/>

/* retheme the control via the semantic tokens */
:root {
  --background: oklch(0.16 0.02 280);
  --foreground: oklch(0.97 0.01 280);
  --ring: oklch(0.72 0.15 280);
}

Frequently asked questions

Is System Segment free for commercial use?
Yes. System Segment is MIT licensed, so you can use it in personal and commercial projects without attribution.
Is System Segment accessible as a dark mode switch?
Yes. The group has radio-group semantics with an accessible name of Theme, each option is a real button with aria-checked and a visible text label beside its decorative icon, and a roving tabindex keeps the group to a single tab stop. Arrow keys move both selection and focus with wrap-around, the focused segment shows its own visible focus ring, and with reduced motion enabled the thumb places instantly with no spring and no crossfade. Only transform and opacity animate, so selection never shifts layout.
How does the system option resolve the actual theme?
System Segment is a controlled input, so it reports the selection and nothing more. When onChange gives you system, resolve it in your app, typically by reading the prefers-color-scheme media query yourself or by passing the value straight to a library such as next-themes, which handles system tracking and persistence for you. Keeping resolution in the consumer means the control works with any theming setup.

Related components

Want the full page this belongs to?Template Empire builds complete Next.js templates with this motion system throughout. Browse the paid templates that pair with these free components.Explore Template Empire templates