Гид по технологиям

Как добавить тёмную тему (dark mode) в React с useState и useEffect

5 min read Frontend Обновлено 17 Dec 2025
Тёмная тема в React с useState и useEffect
Тёмная тема в React с useState и useEffect

Компьютер с открытой веб‑страницей и редактором кода

В последние годы тёмная тема стала стандартной опцией в пользовательских интерфейсах. Она помогает снизить яркость экрана в тёмных условиях, может уменьшать расход батареи на OLED‑экранах и часто воспринимается как более уютный визуальный стиль. Ниже показано несколько надёжных подходов для реализации dark mode в React, начиная с простого варианта и переходя к более зрелым архитектурам.

Краткое описание решения

Мы пройдём через эти шаги:

  • реализуем минимальную версию с useState и useEffect
  • добавим кнопку‑переключатель и CSS для двух тем
  • обеспечим сохранение выбора в localStorage
  • добавим поддержку prefers‑color‑scheme
  • рассмотрим альтернативы: Context API, CSS-переменные, темы на уровне компонентов
  • проверим критерии приёмки, тесты и рекомендации по доступности

Важно: если ваше приложение работает в корпоративной среде с политиками безопасности, уточните правила хранения данных при использовании localStorage.

Зачем нужна тёмная тема

Коротко по преимуществам:

  • уменьшает яркость и потенциальное напряжение глаз в тёмных помещениях
  • может сократить потребление энергии на OLED‑экранах
  • улучшает восприятие контента при низкой освещённости
  • даёт пользователям выбор и повышает удовлетворённость

Но тёмная тема не всегда подходит. В дневном свете тёмные интерфейсы иногда ухудшают читаемость, особенно при плохом цветовом контрасте.

Базовая реализация: useState + useEffect

Это минимально необходимый вариант для быстрого включения тёмной темы.

import React, { useState, useEffect } from 'react'
import './darkMode.css'

function App() {
  const [theme, setTheme] = useState('light')

  const toggleTheme = () => {
    setTheme(prev => (prev === 'light' ? 'dark' : 'light'))
  }

  useEffect(() => {
    document.body.className = theme
  }, [theme])

  return (
    

Пример приложения

) } export default App

Файл CSS (darkMode.css):

.dark {
  background-color: #111;
  color: #e6e6e6;
}
.light {
  background-color: #ffffff;
  color: #111111;
}

/* Базовая анимация для плавной смены темы */
body {
  transition: background-color 200ms ease, color 200ms ease;
}

Пояснение: мы назначаем класс на document.body, чтобы глобально применять стили. Такой подход прост и работает во многих случаях, но имеет ограничения при работе с изолированными компонентами и сторонними UI‑библиотеками.

Сохранение выбора пользователя в localStorage

Чтобы тема сохранялась после перезагрузки, используйте localStorage. Также полезно читать системную настройку как начальное значение.

import React, { useState, useEffect } from 'react'
import './darkMode.css'

function App() {
  const getInitialTheme = () => {
    const saved = localStorage.getItem('theme')
    if (saved) return saved
    const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
    return prefersDark ? 'dark' : 'light'
  }

  const [theme, setTheme] = useState(getInitialTheme)

  const toggleTheme = () => {
    setTheme(prev => (prev === 'light' ? 'dark' : 'light'))
  }

  useEffect(() => {
    localStorage.setItem('theme', theme)
    document.body.className = theme
  }, [theme])

  return (
    

Пример приложения с сохранением темы

) } export default App

Плюсы: пользовательский выбор сохраняется. Минусы: localStorage привязан к конкретному браузеру и извлекает данные только в том же origin, поэтому для синхронизации между устройствами потребуется серверная логика или согласование на аккаунтах.

веб‑страница с кнопкой переключения и включённой светлой темой

Поддержка системной темы через prefers‑color‑scheme

CSS позволяет реагировать на системную тему без JavaScript:

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #111;
    --text: #e6e6e6;
  }
}

@media (prefers-color-scheme: light) {
  :root {
    --bg: #ffffff;
    --text: #111111;
  }
}

body {
  background-color: var(--bg);
  color: var(--text);
}

Этот способ удобен как запасной, но если вы хотите контролировать переключатель внутри приложения и сохранять выбор, комбинируйте его с JavaScript.

Более зрелые подходы и альтернативы

  1. Context API + custom hook
  • Если тема должна быть доступна в глубине дерева компонентов, вынесите логику в ThemeProvider и useTheme hook. Это упрощает инжекцию темы в UI‑библиотеки и сервисы.
  1. CSS‑переменные для токенов темы
  • Используйте CSS‑переменные для базовых цветов, отступов и т. п. Меняя переменные на уровне body, вы получаете адаптацию на стороне стилей без перестроения компонентов.
  1. UI‑библиотеки и design tokens
  • Многие библиотеки (например, MUI, Chakra UI) имеют встроенные механизмы темизации. Рассмотрите миграцию на систему токенов для унификации стилей.
  1. Серверная и синхронизация между устройствами
  • Для синхронизации темы между устройствами привязывайте настройку к аккаунту на сервере. Тогда при входе можно загружать предпочтение пользователя.

Практические советы по доступности и визуальному контрасту

  • Проверяйте контраст текста и фона с помощью инструментов WCAG. Тёмный фон требует достаточной яркости текста для читаемости.
  • Избегайте чистого чёрного и чистого белого в цветах интерфейса, используйте мягкие оттенки для уменьшения зрительной усталости.
  • Убедитесь, что фокусируемые элементы и состояния (hover, active, focus) остаются хорошо видны в обеих темах.

Важно: автоматическое переключение темы при изменении системной настройки может нарушить ожидания пользователей, если они уже явно выбрали тему в приложении. Предпочтительнее сохранять явный выбор и предлагать опцию «следовать системной теме».

Критерии приёмки

  • Переключатель темы переключает стили глобально и локально
  • Выбранная тема сохраняется после перезагрузки
  • В режиме prefers‑color‑scheme тема корректно применяется при отсутствии явного выбора
  • Контраст соответствует рекомендациям WCAG для основных текстовых элементов
  • Нет задержек или мерцания при инициализации темы

Тесты и случаи приёма

  • Тест 1: при отсутствии localStorage и при system dark = true в initial theme открывается тёмная тема
  • Тест 2: после клика по переключателю тема меняется и сохраняется в localStorage
  • Тест 3: при смене системной темы и выключенном ручном контроле интерфейс реагирует через media query
  • Тест 4: проверка таб‑навигации и видимости фокуса в обеих темах

Роль‑ориентированные чеклисты

Для фронтенд‑разработчика:

  • реализовать переключатель и persist в localStorage
  • использовать CSS‑переменные для основных токенов
  • покрыть unit/e2e тестами переключение темы

Для дизайнера:

  • подготовить палитру для light и dark с указанием цветов и контрастов
  • указать состояния элементов и фокусные стили

Для QA:

  • проверить на разных устройствах и браузерах
  • прогнать автоматические проверки контраста

Миграционные заметки и совместимость

  • Если приложение уже использует CSS‑in‑JS, интеграция темы через провайдер библиотеки часто проще, чем глобальные классы body
  • Для серверного рендеринга определите начальную тему на сервере по cookies или по профилю пользователя, чтобы избежать визуального «мерцания» при гидратации

Примеры расширений и когда решение ломается

Когда простого применения className на body достаточно:

  • небольшое SPA без сторонних библиотек

Когда подход может не подойти:

  • крупные приложения с изолированными стилями (css modules, shadow DOM), где глобальный класс body не влияет на компоненты
  • при необходимости синхронизации между устройствами без аккаунтов

Краткая методология внедрения (SOP)

  1. Добавить CSS‑переменные и две темы
  2. Реализовать toggle с useState и сохранением в localStorage
  3. Подключить prefers‑color‑scheme как запасной вариант
  4. Перенести общие токены в root и использовать в компонентах
  5. Добавить тесты и проверки контраста
  6. Документировать поведение для дизайнеров и QA

веб‑страница с кнопкой переключения и включённой тёмной темой

Короткий глоссарий

  • prefers‑color‑scheme — CSS медиа‑фича, которая сообщает предпочтение системы по теме
  • localStorage — браузерное хранилище для сохранения настроек на стороне клиента
  • ThemeProvider — компонент для передачи темы через React Context

Итог

Добавление тёмной темы в React‑приложение можно реализовать быстро и безопасно с помощью useState и useEffect. Для продакшн‑решений рекомендуется учитывать сохранение выбора, поддержку prefers‑color‑scheme, доступность и архитектуру обмена темами через Context или design tokens. Начните с простого варианта и эволюционно переносите логику в ThemeProvider и CSS‑токены по мере роста приложения.

Примечание: при хранении пользовательских предпочтений в localStorage учтите особенности приватности и политики хранения данных в вашей организации.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Переключение учётных записей в Windows 10
Windows

Переключение учётных записей в Windows 10

Как забыть Wi‑Fi сеть в Windows 11
Windows 11

Как забыть Wi‑Fi сеть в Windows 11

ES2 в Logic Pro: полное руководство по синтезатору
Звук

ES2 в Logic Pro: полное руководство по синтезатору

Исправить ошибку inetcpl.cpl в Windows
Windows

Исправить ошибку inetcpl.cpl в Windows

Изменения Remote Raid Passes в Pokémon GO
Игры

Изменения Remote Raid Passes в Pokémon GO

Прозрачность изображений в Google Slides
Презентации

Прозрачность изображений в Google Slides