useReducedMotion

A React hook that detects the user's prefers-reduced-motion OS setting and responds to changes in real time.

Preview

prefers-reduced-motion: no-preference
Hover me — animations enabled

Toggle "Reduce motion" in your OS accessibility settings to see the change in real time.

Installation

npx shadcn@latest add @shadszn/use-reduced-motion

Source

"use client"

import { useEffect, useState } from "react"

export function useReducedMotion(): boolean {
  const [prefersReduced, setPrefersReduced] = useState(false)

  useEffect(() => {
    const mql = window.matchMedia("(prefers-reduced-motion: reduce)")
    setPrefersReduced(mql.matches)

    const handler = (e: MediaQueryListEvent) => setPrefersReduced(e.matches)
    mql.addEventListener("change", handler)
    return () => mql.removeEventListener("change", handler)
  }, [])

  return prefersReduced
}

Usage

"use client"

import { motion } from "framer-motion"
import { useReducedMotion } from "@/hooks/use-reduced-motion"
import { fadeInUp } from "@/lib/motion"

export function AnimatedCard({ children }) {
  const prefersReduced = useReducedMotion()

  return (
    <motion.div
      variants={prefersReduced ? undefined : fadeInUp}
      initial={prefersReduced ? false : "hidden"}
      animate="visible"
    >
      {children}
    </motion.div>
  )
}

Conditional Transitions

const prefersReduced = useReducedMotion()

// Skip spring animations for users who prefer reduced motion
const transition = prefersReduced
  ? { duration: 0 }
  : { type: "spring", stiffness: 300, damping: 20 }

<motion.div
  whileHover={prefersReduced ? undefined : { y: -4 }}
  transition={transition}
>
  Hover card
</motion.div>

Return Value

PropTypeDefaultDescription
prefersReducedbooleanfalseReturns true when the user has enabled "Reduce motion" in their OS accessibility settings. Defaults to false on the server and updates on mount.