Emotion — это библиотека CSS-in-JS для JavaScript и React, которая позволяет писать стили прямо внутри компонентов в виде JavaScript-кода. Если вы встретили в чужом проекте импорт вида @emotion/react или @emotion/styled и непонятный синтаксис с обратными кавычками — это именно она. Библиотека генерирует CSS на лету, присваивает классам уникальные хэш-имена и подставляет стили в документ автоматически.
Подход решает сразу несколько задач: стили живут рядом с компонентом, имена классов не конфликтуют между файлами, а неиспользуемый CSS удаляется вместе с компонентом. Ниже разберём, как устроен Emotion, чем отличаются его два основных API и когда его стоит выбирать вместо обычных CSS-файлов или альтернатив вроде styled-components.
Как устроен CSS-in-JS и место Emotion в этой концепции
Идея CSS-in-JS проста: вместо отдельного файла styles.css описание стилей хранится в JS-объекте или шаблонной строке. Библиотека во время выполнения (или на этапе сборки) превращает это описание в настоящие CSS-правила и вставляет их в <style>-тег внутри документа.
Emotion — одна из самых распространённых реализаций этого подхода. Она состоит из нескольких пакетов: ядро @emotion/css работает без фреймворков, а @emotion/react и @emotion/styled добавляют интеграцию с React. Отдельный пакет @emotion/cache управляет вставкой стилей и позволяет настраивать префиксы, порядок правил и SSR.
Ключевая особенность — сериализация стилей: Emotion вычисляет хэш от содержимого стиля и использует его как имя класса. Одинаковые стили получают одинаковый класс, поэтому дубли в итоговом CSS исключаются автоматически.
Emotion превращает JS-описание стилей в реальный CSS с уникальными хэш-классами и вставляет его в документ автоматически — без ручного управления именами классов.
Два основных API: css-проп и styled
Первый способ — css-проп. Вы передаёте стили прямо в атрибут css любого JSX-элемента, а Emotion преобразует их в класс:
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
const buttonStyle = css`
background-color: #3b82f6;
color: white;
padding: 8px 16px;
`;
function Button() {
return <button css={buttonStyle}>Нажми меня</button>;
}
Второй способ — styled-компоненты через пакет @emotion/styled. Создаётся новый компонент с привязанными стилями, который можно переиспользовать и расширять через пропсы:
import styled from '@emotion/styled';
const Title = styled.h1`
font-size: 24px;
color: ${props => props.primary ? '#3b82f6' : '#111'};
`;
Оба подхода можно смешивать в одном проекте. Практическое правило: css-проп удобен для разовых локальных правок, а styled — для переиспользуемых элементов дизайн-системы.
Установка и базовая настройка
Для React-проекта на Vite или Create React App достаточно поставить два пакета. Команда установки через npm:
npm install @emotion/react @emotion/styled
После установки styled-API работает сразу, без дополнительной конфигурации. Для css-пропа нужно либо добавить pragma-комментарий /** @jsxImportSource @emotion/react */ в начало файла, либо один раз настроить JSX-трансформацию в конфиге сборщика. В Vite это делается через опцию jsxImportSource в плагине React, в проектах с Babel — через пресет или плагин Emotion.
☑️ Проверка корректной настройки Emotion
⚠️ Внимание: если css-проп «не работает» и стили не применяются, чаще всего причина в отсутствующей настройке JSX-трансформации, а не в синтаксисе. Сначала проверьте конфиг сборщика, а не код компонента.
Темизация и динамические стили
Emotion включает провайдер темы ThemeProvider. Объект темы спускается по дереву компонентов, и любой styled-компонент получает к нему доступ через props.theme. Это удобно для единой палитры, отступов и переключения светлой/тёмной темы.
Динамические значения задаются функциями внутри шаблонной строки — как в примере с props.primary выше. Стоит учитывать: каждое уникальное сочетание динамических значений порождает новый класс. Если значение меняется часто (например, координата при анимации), разумнее использовать CSS-переменные через style-атрибут, а не генерировать сотни классов.
- 🎨 ThemeProvider — централизованная тема для всего дерева компонентов
- 🔀 Функции в шаблонах — стили, зависящие от пропсов компонента
- 🧩 Композиция — объединение нескольких css-объектов в массиве
css={[base, active]} - 🌗 CSS-переменные — для часто меняющихся значений без генерации новых классов
Сравнение с альтернативами
Emotion часто сравнивают с styled-components и с обычными CSS-модулями. У каждого подхода свои сильные стороны, и выбор зависит от требований проекта.
| Критерий | Emotion | styled-components | CSS Modules |
|---|---|---|---|
| Синтаксис | css-проп и styled | только styled | отдельные .css-файлы |
| Работа вне React | да, через @emotion/css | нет | да, с любым фреймворком |
| Генерация CSS | в рантайме | в рантайме | на этапе сборки |
| Настройка кэша и SSR | гибкая, через @emotion/cache | базовая | не требуется |
| Зависимость от JS-рантайма | да | да | нет |
Главное практическое отличие Emotion — наличие двух API и фреймворк-независимого ядра, тогда как styled-components привязан только к React и одному синтаксису. CSS Modules, в свою очередь, не требует JS вообще, но лишён динамики на уровне пропсов.
Если проект уже использует Material UI (MUI), Emotion у вас скорее всего уже установлен — MUI использует его как движок стилей по умолчанию, и css-проп доступен без отдельной настройки.
Производительность и типичные проблемы
Поскольку стили генерируются в рантайме, при очень большом количестве динамических классов возможны накладные расходы на сериализацию и вставку правил. На практике для типовых интерфейсов это незаметно, но есть приёмы, которые помогают избежать проблем:
- ⚡ Выносите статичные стили за пределы компонента, чтобы объект не создавался заново при каждом рендере
- 📦 Не создавайте styled-компоненты внутри тела другого компонента — это приводит к перемонтированию
- 🧪 Для SSR проверяйте корректное извлечение критического CSS на стороне сервера
⚠️ Внимание: объявление styled-компонента внутри рендера другого компонента — распространённая ошибка. При каждом рендере создаётся новый компонент, React теряет состояние вложенных элементов, а DOM перемонтируется. Выносите styled-объявления на уровень модуля.
Как Emotion работает с SSR
При серверном рендеринге стили нужно извлечь и вставить в HTML до отправки клиенту, иначе страница отобразится без стилей до загрузки JS. Emotion поддерживает несколько режимов: стандартный (стили встраиваются рядом с элементами) и извлечение критического CSS через пакет @emotion/server. Конкретная настройка зависит от фреймворка — для Next.js, Remix и собственного сервера шаги различаются, поэтому сверяйтесь с официальной документацией вашей связки.
Когда Emotion подходит, а когда лучше обойтись без него
Emotion оправдан в компонентных проектах с переиспользуемым UI, дизайн-системой, темизацией и стилями, зависящими от состояния. Он хорошо сочетается с React-экосистемой и библиотеками вроде MUI.
Для простого статичного сайта или лендинга подключение CSS-in-JS — избыточное усложнение: обычный CSS или CSS Modules справятся без рантайм-зависимости и дополнительного веса бандла. Также стоит оценить альтернативы с нулевым рантаймом, если критична производительность на слабых устройствах.
Выбирайте Emotion для компонентных React-проектов с динамическими стилями и темизацией; для статичных страниц проще и легче обычный CSS или CSS Modules.
Частые вопросы
Чем Emotion отличается от styled-components?
Основные отличия: у Emotion есть два API (css-проп и styled), фреймворк-независимое ядро @emotion/css и более гибкая настройка кэша через @emotion/cache. styled-components предлагает только styled-синтаксис и работает только с React.
Работает ли Emotion без React?
Да. Пакет @emotion/css предоставляет функцию css(), которая возвращает имя класса и вставляет стили в документ. Его можно использовать с ванильным JavaScript или другими фреймворками.
Почему css-проп не применяется к элементу?
Наиболее вероятная причина — не настроена JSX-трансформация. Добавьте pragma /** @jsxImportSource @emotion/react */ в начало файла или укажите jsxImportSource в конфиге сборщика. Также проверьте, что элемент не является сторонним компонентом, который не пробрасывает проп className.
Влияет ли Emotion на размер бандла?
Да, библиотека добавляет вес в итоговый бандл, как и любая рантайм-зависимость. Точный размер зависит от используемых пакетов и настроек сборки — проверить его можно через анализатор бандла вашего сборщика.
Можно ли использовать Emotion вместе с Tailwind или обычным CSS?
Да, они сосуществуют без конфликтов: Emotion генерирует уникальные классы, которые не пересекаются с глобальными стилями. Важно лишь контролировать порядок подключения стилей, чтобы специфичность работала ожидаемо.