minhvo.vercel.app

Wed Sep 17 2025

react-theme-switch-animation

react-theme-switch-animation

React Hook for smooth dark/light theme transitions using the View Transitions API — circle, blur-circle, and QR-scan effects.

Overview

A tiny React Hook (≈8 KB) that turns the dark/light toggle into a cinematic reveal, powered by the browser's View Transitions API. Currently 57+ stars on GitHub and shipping in production sites including this one.

Usage

'use client'
import { useModeAnimation, ThemeAnimationType } from 'react-theme-switch-animation'
 
export default function ThemeToggle() {
  const { ref, toggleSwitchTheme, isDarkMode } = useModeAnimation({
    animationType: ThemeAnimationType.CIRCLE,
    duration: 750,
  })
 
  return (
    <button ref={ref} onClick={toggleSwitchTheme}>
      {isDarkMode ? '🌙' : '☀️'}
    </button>
  )
}

Animation Types

TypeEffect
CIRCLECrisp circular reveal expanding from the toggle button to the farthest viewport corner
BLUR_CIRCLESoft-edged radial reveal using an SVG Gaussian-blur mask
QR_SCANHorizontal "scan line" wipe reminiscent of a QR scanner

Technical Highlights

  • Origin-aware reveal: uses getBoundingClientRect() on the toggle button, then computes Math.hypot to all four viewport corners — the animation always covers the full viewport regardless of where the button lives
  • document.startViewTransition for the snapshot, flushSync to commit the theme class inside the transition frame, then documentElement.animate({ clipPath: ... }, { pseudoElement: '::view-transition-new(root)' }) drives the reveal — no <style> injection required for the CIRCLE variant
  • High-DPI adaptation: at ≥3000×2000 it shaves animation duration by 20%, enables backface-visibility and translate3d hacks for smoother GPU compositing
  • Accessibility: respects prefers-reduced-motion — gracefully degrades to an instant theme swap
  • External state: isDarkMode + onDarkModeChange props let the hook drop into existing state managers (next-themes, Redux, Jotai, etc.)

Stack

  • TypeScript library compiled to ESM + CJS
  • Zero runtime dependencies beyond React
  • Tested against React 16.8+ on Next.js 13/14/15, CRA, and Vite

Role & Responsibilities

  • Authored the hook, the three animation strategies, and the High-DPI adaptation
  • Wrote the docs, the demo app, and shipped to npm
  • Maintain the issue tracker and review community PRs