Миграция со styled-components и emotion

Принципы работы MUI с стилями

MUI (Material-UI) предлагает несколько способов стилизации компонентов, среди которых наиболее актуальны:

  • sx проп – краткий и мощный способ задавать стили на уровне компонента. Позволяет использовать объектную запись, похожую на CSS-in-JS, с поддержкой темизации.
  • styled API – совместимый с Emotion и styled-components подход, который позволяет создавать переиспользуемые стилизованные компоненты.
  • makeStyles и withStyles – устаревающие, но все еще применяемые в существующих проектах методы, подходящие для пошаговой миграции.

Главная идея при миграции заключается в том, что MUI интегрирует стили в компоненты на уровне их API, что упрощает поддержку темы и сокращает необходимость создавать отдельные файлы стилей.

Перевод styled-components в MUI styled

Рассмотрим типичный компонент на styled-components:

import styled from 'styled-components';

const Button = styled.button`
  background-color: ${(props) => props.primary ? 'blue' : 'gray'};
  color: white;
  padding: 8px 16px;
  border-radius: 4px;
  &:hover {
    opacity: 0.8;
  }
`;

Его аналог на MUI будет выглядеть так:

import { styled } from '@mui/material/styles';
import ButtonBase from '@mui/material/Button';

const Button = styled(ButtonBase)(({ theme, primary }) => ({
  backgroundColor: primary ? theme.palette.primary.main : theme.palette.grey[500],
  color: theme.palette.common.white,
  padding: '8px 16px',
  borderRadius: 4,
  '&:hover': {
    opacity: 0.8,
  },
}));

Ключевые отличия:

  • Использование theme для доступа к цветовой палитре и другим параметрам темы.
  • Свойства передаются через объект вместо CSS-шаблонной строки.
  • styled из MUI работает совместимо с Emotion под капотом, что облегчает плавный переход.

Перевод Emotion css и jsx в sx и styled

Компоненты, написанные с использованием Emotion css:

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

const style = (primary) => css`
  background-color: ${primary ? 'blue' : 'gray'};
  color: white;
  padding: 8px 16px;
  border-radius: 4px;
`;

export const Button = ({ primary, children }) => (
  <button css={style(primary)}>{children}</button>
);

На MUI можно переписать двумя способами:

Через sx проп:

import Button from '@mui/material/Button';

export const Button = ({ primary, children }) => (
  <Button
    sx={{
      backgroundColor: primary ? 'primary.main' : 'grey.500',
      color: 'common.white',
      p: '8px 16px',
      borderRadius: 1,
      '&:hover': {
        opacity: 0.8,
      },
    }}
  >
    {children}
  </Button>
);

Через styled API:

import { styled } from '@mui/material/styles';
import ButtonBase from '@mui/material/Button';

const Button = styled(ButtonBase, {
  shouldForwardProp: (prop) => prop !== 'primary',
})(({ theme, primary }) => ({
  backgroundColor: primary ? theme.palette.primary.main : theme.palette.grey[500],
  color: theme.palette.common.white,
  padding: '8px 16px',
  borderRadius: 4,
  '&:hover': {
    opacity: 0.8,
  },
}));

Примечания при миграции:

  • shouldForwardProp предотвращает передачу пользовательских пропов в DOM, что критично при использовании нестандартных атрибутов.
  • Для краткой стилизации компонентов без создания отдельного styled-компонента чаще используют sx проп.
  • При сложной логике стилей лучше использовать styled, чтобы сохранять чистоту кода и переиспользуемость.

Работа с темой и palette

MUI интегрирует систему тем, которая позволяет полностью отказаться от магических цветов в коде. Пример переноса темы:

import { createTheme } from '@mui/material/styles';

const theme = createTheme({
  palette: {
    primary: {
      main: '#1976d2',
    },
    secondary: {
      main: '#dc004e',
    },
    grey: {
      500: '#9e9e9e',
    },
  },
});

export default theme;

В styled-components или Emotion тема использовалась через ThemeProvider и хук useTheme. В MUI подход схож, но объект темы уже предоставляет доступ к Material Design palette, типам и размерам компонентов, упрощая единообразие интерфейса.

Миграция глобальных стилей

Для глобальных CSS, ранее применяемых через createGlobalStyle (styled-components) или Global (Emotion), MUI предлагает GlobalStyles:

import { GlobalStyles } from '@mui/styled-engine';

<GlobalStyles
  styles={{
    body: {
      margin: 0,
      padding: 0,
      fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
      backgroundColor: '#f5f5f5',
    },
  }}
/>

Особенности:

  • GlobalStyles интегрируется с темой и позволяет использовать переменные theme.palette и theme.spacing.
  • Поддерживается объектная запись стилей, без необходимости писать шаблонные строки.

Адаптивная стилизация

MUI позволяет задавать адаптивные стили через объектную структуру:

<Box
  sx={{
    width: {
      xs: '100%', // до small
      sm: '50%',  // от small и выше
      md: '25%',  // от medium и выше
    },
    p: 2,
  }}
/>

Это заменяет медиазапросы в styled-components и Emotion и упрощает поддержку адаптивности без внешних зависимостей.

Пошаговая стратегия миграции

  1. Определение компонентов, требующих миграции – начать с маленьких, переиспользуемых компонентов.
  2. Замена styled-components/Emotion на sx для простых случаев – быстрый выигрыш.
  3. Использование styled для сложных компонентов с логикой пропсов – сохраняет читаемость.
  4. Перенос темы и палитры – объединение всех цветов, отступов и шрифтов в объект темы MUI.
  5. Рефакторинг глобальных стилей через GlobalStyles – упрощение и интеграция с темой.
  6. Постепенная замена медиазапросов на адаптивные свойства в sx – уменьшение CSS-файлов и inline медиазапросов.
  7. Удаление зависимостей styled-components и Emotion – завершение миграции после проверки стабильности.

Особенности работы с динамическими пропсами

В MUI пропсы, влияющие на стиль, передаются в styled через функцию:

const Card = styled(Box)(({ theme, highlighted }) => ({
  backgroundColor: highlighted ? theme.palette.primary.light : theme.palette.background.paper,
  border: `1px solid ${theme.palette.divider}`,
}));

Рекомендации:

  • Не передавать нестандартные пропсы напрямую в DOM.
  • Использовать объект theme для всех цветов, шрифтов и отступов.
  • Для условной стилизации компонентов с большим количеством веток лучше разбивать стили на функции.

Итоговые преимущества MUI

  • Единая интеграция с темой и palette.
  • Сокращение boilerplate кода для адаптивных и динамических стилей.
  • Полная совместимость с Emotion под капотом, что облегчает миграцию.
  • Поддержка sx для быстрой стилизации и styled для переиспользуемых компонентов.
  • Упрощение глобальной стилизации через GlobalStyles.

Это позволяет постепенно отказаться от styled-components и Emotion, сохраняя логическую структуру компонентов и повышая согласованность интерфейса.