Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Typescript | How can I enable dark mode using tailwind?

I got stuck at enabling dark mode for my react/typescript element. I created a Context.Provider to switch light/dark mode for the entire app, but the toggle does not work at all. If anybody knows how to fix it, please help.

This is the ThemeContext and ContextProvider

import { createContext, useState, useEffect } from 'react'

type ThemeName = 'light' | 'dark' | string
type ThemeContextType = {
  theme: ThemeName
  setTheme: (name: ThemeName) => void
}

const getInitialTheme = () => {
  if (typeof window !== 'undefined' && window.localStorage) {
    const storedPrefs = window.localStorage.getItem('color-theme')
    if (typeof storedPrefs === 'string') {
      return storedPrefs
    }

    const userMedia = window.matchMedia('(prefers-color-scheme:dark)')
    if (userMedia.matches) {
      return 'dark'
    }
  }
  // returning default theme here
  return 'light'
}

export const ThemeContext = createContext<ThemeContextType>({} as ThemeContextType)

export const ThemeProvider = ({ initialTheme, children }) => {
  const [theme, setTheme] = useState(getInitialTheme)


  const rawSetTheme = theme => {
//Updated rawSetTheme to theme above//
    const root = window.document.documentElement
    const isDark = theme === 'dark'

    root.classList.remove(isDark ? 'light' : 'dark')
    root.classList.add(theme)

    localStorage.setItem('color-theme', theme)
  }

  if (initialTheme) {
    rawSetTheme(initialTheme)
  }

  useEffect(() => {
    rawSetTheme(theme)
  }, [theme])

  return <ThemeContext.Provider value={{ theme, setTheme }}>{children}</ThemeContext.Provider>
}

And this is the index.tsx.

ReactDOM.render(
  <ThemeProvider>
    <App />, document.getElementById('root')
  </ThemeProvider>
)

And this is the Toggle

export const DarkModeToggle: VFC<Props> = memo(() => {
  const { theme, setTheme } = useContext(ThemeContext)

  function isDark() {
    return theme === 'dark'
  }

  function toggleTheme(e) {
    setTheme(e.target.checked ? 'dark' : 'light')
  }
  return (
    <div className='flex flex-col'>
      <label htmlFor='unchecked' className='mt-3 inline-flex items-center cursor-pointer'>
        <span className='relative'>
          <span className='block w-10 h-6 bg-gray-200 rounded-full shadow-inner'></span>
          <span
            className={`${
              theme === 'dark' ? 'bg-indigo-400 transform translate-x-full' : 'bg-white'
            } absolute block w-4 h-4 mt-1 ml-1  rounded-full shadow inset-y-0 left-0 focus-within:shadow-outline transition-transform duration-300 ease-in-out`}
          >
            <input
              id='darkmode'
              onClick={e => toggleTheme(e)}
              type='checkbox'
              checked={isDark()}
              className='absolute opacity-0 w-0 h-0'
            />
          </span>
        </span>
        <span className='ml-3 text-sm'>{theme === 'dark' ? 'ON' : 'OFF'}</span>
      </label>
    </div>
  )
})

Updated: I changed the 'rawSetTheme' to 'theme' for variable, but it returns an error" in App.tsx as below. If you have any suggestions, it would be very appreciated.

Property 'initialTheme' is missing in type '{ children: Element; }' but required in type '{ initialTheme: any; children: any; }'.  TS2741

     7 | export default function App() {
     8 |   return (
  >  9 |     <ThemeProvider>
       |      ^
    10 |       <Router />
    11 |     </ThemeProvider>
    12 |   )
like image 513
Chi Avatar asked Feb 26 '26 17:02

Chi


1 Answers

Thank you for the comment. I could fix it. It was just a small error of Toggle.tsx. The tailwind extends setting is also working fine.

export const DarkModeToggle: VFC<Props> = memo(() => {
  const { theme, setTheme } = useContext(ThemeContext)
  
  return (

    <div className='flex flex-col'>
      <label className='mt-3 inline-flex items-center cursor-pointer'>
        <span className='relative'>
          <span className='block w-10 h-6 bg-gray-200 rounded-full shadow-inner'></span>
          <span
            className={`${
              theme === 'dark' ? 'bg-indigo-400 transform translate-x-full' : 'bg-white'
            } absolute block w-4 h-4 mt-1 ml-1  rounded-full shadow inset-y-0 left-0 focus-within:shadow-outline transition-transform duration-300 ease-in-out`}
          >
            <input onClick={()=>setTheme(theme==='dark'?'light':'dark')} className='absolute opacity-0 w-0 h-0' />
          </span>
        </span>
        <span className='ml-3 text-sm'>{theme === 'dark' ? 'ON' : 'OFF'}</span>
      </label>
    </div>
  )
})
like image 114
Chi Avatar answered Feb 28 '26 08:02

Chi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!