Skip to content

MinhOmega/react-theme-switch-animation

Repository files navigation

React Theme Switch Animation Hook

Beautiful, smooth animations for theme switching in React applications. Built with TypeScript and powered by the View Transition API.

πŸš€ Live Demo | πŸ“¦ npm Package | πŸ’Ό Hire me

πŸŽ₯ Demo

✨ Six Animation Types Available

  • πŸ”΅ Circle: Smooth expanding circle transition
  • πŸŒ€ Blur Circle: Circle with elegant blur effect on the edges
  • πŸ“± QR Scan: Scanning line sweeps left to right (like QR code reader)
  • πŸ”· Polygon: Diagonal wipe that sweeps across the screen
  • πŸ“ Polygon Gradient: Diagonal wipe with a soft gradient edge
  • 🎬 Custom GIF: Reveal the new theme through any GIF mask of your choice

πŸ“ Notes

  • The hook is only available in the browser environment. So if you use NextJS App router or any other framework that uses Server Components, you should use this hook in a Client Component by adding the directive use client
  • Currently works only if the project is using TailwindCSS
  • Ensure your project has the necessary TailwindCSS configuration for dark mode

πŸš€ Features

  • 🎨 Multiple Animation Types: Circle, Blur Circle, QR Scan, Polygon, Polygon Gradient, and Custom GIF animations
  • ⚑ High Performance: Optimized for high-resolution displays with smooth 60fps animations
  • 🎯 View Transition API: Leverages modern browser APIs for seamless transitions
  • πŸ“± Responsive Design: Works perfectly across all device sizes and screen resolutions
  • β™Ώ Accessibility First: Respects prefers-reduced-motion and provides fallback experiences
  • πŸ”§ TypeScript Support: Full TypeScript support for enhanced development experience
  • πŸ’Ύ State Persistence: Uses localStorage to persist theme state across sessions
  • πŸŽ›οΈ Highly Customizable: Configure duration, easing, blur amount, and more
  • πŸͺ React Hooks: Clean, modern React Hooks API

πŸ“¦ Installation

Install the package using npm or YARN:

npm install react-theme-switch-animation

or

yarn add react-theme-switch-animation

πŸ“š Usage

Here’s how to use the useModeAnimation hook in your React component:

'use client'

import React from 'react'
import { useModeAnimation } from 'react-theme-switch-animation'

const MyComponent = () => {
  const { ref, toggleSwitchTheme, isDarkMode } = useModeAnimation()

  return (
    <button ref={ref} onClick={toggleSwitchTheme}>
      Toggle Dark Mode (Currently {isDarkMode ? 'Dark' : 'Light'} Mode)
    </button>
  )
}

export default MyComponent

πŸ“š API

useModeAnimation accepts an optional props object with the following properties:

Property Type Default Description
duration number 750 Duration of the animation in milliseconds (defaults to 1500 for POLYGON_GRADIENT and 2000 for GIF).
easing string "ease-in-out" CSS easing for the animation (defaults to an expo curve for POLYGON, POLYGON_GRADIENT, and GIF).
pseudoElement string "::view-transition-new(root)" Pseudo-element used for the animation.
globalClassName string "dark" Class name to apply to the root element.
animationType ThemeAnimationType ThemeAnimationType.CIRCLE Type of animation effect (CIRCLE, BLUR_CIRCLE, QR_SCAN, POLYGON, POLYGON_GRADIENT, GIF)
blurAmount number 2 Blur intensity for blur circle animation.
gifUrl string undefined URL of the GIF used as the reveal mask (required for the GIF animation type).
styleId string "theme-switch-style" ID for the injected style element (blur circle, polygon gradient, and gif animations).
isDarkMode boolean false Initial dark mode state.
onDarkModeChange (isDark: boolean) => void undefined Callback function to handle dark mode change.

Animation Types

The hook supports six types of animations:

  • ThemeAnimationType.CIRCLE: A clean circle expansion animation (default)
  • ThemeAnimationType.BLUR_CIRCLE: A circle expansion with blur effect on the edges
  • ThemeAnimationType.QR_SCAN: A scanning line that sweeps from left to right
  • ThemeAnimationType.POLYGON: A diagonal wipe β€” toward dark it sweeps from the top-left corner, back to light from the bottom-right
  • ThemeAnimationType.POLYGON_GRADIENT: A diagonal wipe with a soft gradient edge expanding from the top-left corner
  • ThemeAnimationType.GIF: Reveals the new theme through a custom GIF mask that scales up to fill the screen (requires gifUrl)

Examples for Each Animation Type

Circle Animation (Default)

'use client'

import React from 'react'
import { ThemeAnimationType, useModeAnimation } from 'react-theme-switch-animation'

const CircleAnimation = () => {
  const { ref, toggleSwitchTheme, isDarkMode } = useModeAnimation({
    animationType: ThemeAnimationType.CIRCLE,
  })

  return (
    <button ref={ref} onClick={toggleSwitchTheme}>
      {isDarkMode ? 'πŸŒ™' : 'β˜€οΈ'} Toggle Theme
    </button>
  )
}

Blur Circle Animation

'use client'

import React from 'react'
import { ThemeAnimationType, useModeAnimation } from 'react-theme-switch-animation'

const BlurCircleAnimation = () => {
  const { ref, toggleSwitchTheme, isDarkMode } = useModeAnimation({
    animationType: ThemeAnimationType.BLUR_CIRCLE,
    blurAmount: 4, // Optional: adjust blur intensity
    duration: 1000, // Optional: adjust animation duration
  })

  return (
    <button ref={ref} onClick={toggleSwitchTheme}>
      {isDarkMode ? 'πŸŒ™' : 'β˜€οΈ'} Toggle Theme (Blur)
    </button>
  )
}

QR Scan Animation

'use client'

import React from 'react'
import { ThemeAnimationType, useModeAnimation } from 'react-theme-switch-animation'

const QRScanAnimation = () => {
  const { ref, toggleSwitchTheme, isDarkMode } = useModeAnimation({
    animationType: ThemeAnimationType.QR_SCAN,
    duration: 500, // Faster scan animation
  })

  return (
    <button ref={ref} onClick={toggleSwitchTheme}>
      {isDarkMode ? 'πŸŒ™' : 'β˜€οΈ'} Toggle Theme (QR Scan)
    </button>
  )
}

Polygon Animation

'use client'

import React from 'react'
import { ThemeAnimationType, useModeAnimation } from 'react-theme-switch-animation'

const PolygonAnimation = () => {
  const { ref, toggleSwitchTheme, isDarkMode } = useModeAnimation({
    animationType: ThemeAnimationType.POLYGON,
  })

  return (
    <button ref={ref} onClick={toggleSwitchTheme}>
      {isDarkMode ? 'πŸŒ™' : 'β˜€οΈ'} Toggle Theme (Polygon)
    </button>
  )
}

Polygon Gradient Animation

'use client'

import React from 'react'
import { ThemeAnimationType, useModeAnimation } from 'react-theme-switch-animation'

const PolygonGradientAnimation = () => {
  const { ref, toggleSwitchTheme, isDarkMode } = useModeAnimation({
    animationType: ThemeAnimationType.POLYGON_GRADIENT,
    duration: 1500, // Optional: defaults to 1500ms for this animation
  })

  return (
    <button ref={ref} onClick={toggleSwitchTheme}>
      {isDarkMode ? 'πŸŒ™' : 'β˜€οΈ'} Toggle Theme (Polygon Gradient)
    </button>
  )
}

Custom GIF Animation

'use client'

import React from 'react'
import { ThemeAnimationType, useModeAnimation } from 'react-theme-switch-animation'

const GifAnimation = () => {
  const { ref, toggleSwitchTheme, isDarkMode } = useModeAnimation({
    animationType: ThemeAnimationType.GIF,
    gifUrl: 'https://media.tenor.com/cyORI7kwShQAAAAi/shigure-ui-dance.gif', // Any GIF URL
    duration: 2000, // Optional: defaults to 2000ms for this animation
  })

  return (
    <button ref={ref} onClick={toggleSwitchTheme}>
      {isDarkMode ? 'πŸŒ™' : 'β˜€οΈ'} Toggle Theme (GIF)
    </button>
  )
}

Tip: GIFs with transparent backgrounds work best as masks β€” the theme is revealed through the opaque parts of the GIF. If gifUrl is omitted, the hook falls back to the circle animation.

Returns an object containing:

  • ref: React ref for attaching to the component that will trigger the dark mode toggle.
  • toggleSwitchTheme: Function to toggle dark mode.
  • isDarkMode: Current state of dark mode (true for dark, false for light).

⚑ Performance & Optimization

This library is built with performance in mind:

  • High-Resolution Display Support: Automatically detects and optimizes for displays β‰₯3000px width/height
  • Adaptive Animation Duration: Reduces duration by 20% on high-res displays for smoother experience
  • GPU Acceleration: Uses hardware-accelerated CSS properties (transform, clip-path, mask)
  • Memory Efficient: Automatic cleanup of style elements and event listeners
  • Accessibility: Respects prefers-reduced-motion for users who prefer minimal animations

🌐 Browser Support

  • Modern Browsers: Chrome 111+, Firefox 103+, Safari 16.4+
  • View Transition API: Falls back gracefully on unsupported browsers
  • Progressive Enhancement: Basic theme switching works everywhere, animations enhance the experience

πŸ“ Requirements

  • React 16.8 or later (for Hooks support)
  • TypeScript for compiling the package during installation
  • TailwindCSS for styling (ensure dark mode is configured)

πŸ™ Acknowledgements

The polygon, polygon gradient, and GIF animations are inspired by rudrodip/theme-toggle-effect.

🀝 Contributing

Contributions are welcome! Please open an issue or submit a pull request with your suggested changes.

πŸ“œ License

MIT

About

React Theme Switch with smooth animation supports dark and light modes on NextJS, ReactJS, and more ...

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors