usePrefersReducedMotion


For some people, motion can be harmful.

The prefers-reduced-motion CSS media query allows us to disable animations for these folks. For our animations that are entirely in CSS (eg. CSS transitions, CSS keyframe animations), this works great 💯

What about for our animations in JavaScript, though? For example, when we use a library like React Spring or Framer Motion? We need to manage it ourselves, and it becomes a pretty tricky endeavour.

For these cases, is the use-prefers-reduced-motion hook.

Usage

import { usePrefersReducedMotion } from 'reactchemy'

export default function Component() {
   const prefersReducedMotion = usePrefersReducedMotion()

   return (
     <div>
       <h2>Prefers Reduced Motion Demo</h2>
       {prefersReducedMotion
          ? (
         <p>Reduced motion is preferred.</p>
             )
          : (
         <p>Reduced motion is not preferred.</p>
             )}
     </div>
   )
}

Hook

import { useEffect, useState } from 'react'

const QUERY = '(prefers-reduced-motion: no-preference)'
const isRenderingOnServer = typeof window === 'undefined'

function getInitialState() {
   // For our initial server render, we won't know if the user
   // prefers reduced motion, but it doesn't matter. This value
   // will be overwritten on the client, before any animations
   // occur.
   return isRenderingOnServer ? true : !window.matchMedia(QUERY).matches
}

export function usePrefersReducedMotion() {
   const [prefersReducedMotion, setPrefersReducedMotion] = useState(
      getInitialState,
   )
   useEffect(() => {
      const mediaQueryList = window.matchMedia(QUERY)
      const listener = (event: MediaQueryListEvent) => {
         setPrefersReducedMotion(!event.matches)
      }
      if (mediaQueryList.addEventListener)
         mediaQueryList.addEventListener('change', listener)

      else
         mediaQueryList.addListener(listener)

      return () => {
         if (mediaQueryList.removeEventListener)
            mediaQueryList.removeEventListener('change', listener)

         else
            mediaQueryList.removeListener(listener)
      }
   }, [])
   return prefersReducedMotion
}