CSS-in-JS: Styled Components, Emotion

CSS-in-JS — подход к стилизации, при котором CSS пишется прямо в JavaScript-файле и привязывается к компоненту; наиболее популярные библиотеки — Styled Components и Emotion.

Зачем нужно

CSS-in-JS решает проблему глобального пространства имён CSS и позволяет использовать всю мощь JavaScript для динамических стилей: условные правила на основе пропсов, темизация через context, автоматическое удаление неиспользуемых стилей. Библиотеки генерируют уникальные классы в runtime, исключая конфликты. Это особенно удобно для дизайн-систем с полной темизацией.

Где используется

  • React-приложения с динамической темизацией (Material UI v5 использует Emotion)
  • Дизайн-системы, где стили сильно зависят от пропсов и темы
  • Команды, предпочитающие держать стили и логику компонента в одном файле

Styled Components

import styled from 'styled-components';

// Создание стилизованного компонента
const Button = styled.button`
  padding: 8px 16px;
  border-radius: 4px;
  font-size: 14px;
  cursor: pointer;

  /* Динамические стили на основе пропсов */
  background: ${({ variant }) =>
    variant === 'primary' ? '#0070f3' : 'transparent'};
  color: ${({ variant }) =>
    variant === 'primary' ? 'white' : '#0070f3'};
  border: ${({ variant }) =>
    variant === 'primary' ? 'none' : '1px solid #0070f3'};

  &:hover {
    opacity: 0.8;
  }

  &:disabled {
    opacity: 0.4;
    cursor: not-allowed;
  }
`;

// Использование
function App() {
  return (
    <>
      <Button variant="primary">Сохранить</Button>
      <Button variant="secondary">Отмена</Button>
    </>
  );
}

Темизация через ThemeProvider

import { ThemeProvider, styled } from 'styled-components';

const theme = {
  colors: {
    primary: '#0070f3',
    background: '#ffffff',
    text: '#333333',
  },
  spacing: {
    sm: '8px',
    md: '16px',
    lg: '24px',
  },
};

const Card = styled.div`
  background: ${({ theme }) => theme.colors.background};
  padding: ${({ theme }) => theme.spacing.md};
  color: ${({ theme }) => theme.colors.text()};
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
`;

function App() {
  return (
    <ThemeProvider theme={theme}>
      <Card>Содержимое карточки</Card>
    </ThemeProvider>
  );
}

Emotion — альтернатива

/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
import styled from '@emotion/styled';

// Вариант 1: css prop
const buttonStyle = css`
  padding: 8px 16px;
  background: #0070f3;
  color: white;
`;

function Button({ children }) {
  return <button css={buttonStyle}>{children}</button>;
}

// Вариант 2: styled (идентично Styled Components)
const StyledButton = styled.button`
  padding: 8px 16px;
  background: #0070f3;
`;

Частые ошибки

  • CSS-in-JS в SSR без настройки — без серверного extracta стили вставляются после JS, что вызывает мерцание (FOUC); нужно настроить ServerStyleSheet (styled-components) или cache (emotion).
  • Лишние ре-рендеры из-за инлайн-объектов — передача объекта стилей напрямую как пропс создаёт новый объект на каждый рендер.
  • Накладные расходы в runtime — CSS-in-JS генерирует стили в JS; для критичных по производительности приложений рассмотрите CSS Modules или zero-runtime решения (Linaria, Vanilla Extract).

Связанные темы

Ресурсы