Компоненты React стилизуют обычным CSS. Дайте элементам класс через проп className и напишите правила в таблице стилей, используйте проп style для значений из JavaScript и берите CSS Modules или Tailwind, когда проект растёт. Пример ниже задаёт CSS в элементе <style>, чтобы его можно было запустить на этой странице; в проекте он лежал бы в файле .css.
Замените #2563eb на #16a34a в правиле .btn, и кнопка станет зелёной. Всё, что вы знаете о CSS, применимо: селекторы, медиазапросы, состояния при наведении, анимации.
className, а не class
JSX компилируется в JavaScript, где class это зарезервированное слово, поэтому React использует className, имя, которое сам DOM использует для этого свойства (element.className). Браузер всё равно получает обычный атрибут class. То же переименование действует для htmlFor вместо for у подписей. React 19 принимает class с предупреждением в разработке, но правильное имя className, и его использует любой проект.
className принимает строку, поэтому несколько классов разделяются пробелом: className="btn btn-primary". Поскольку значение это JavaScript, его можно вычислить.
Условные классы
Чтобы добавить класс только в некоторых случаях, соберите строку тернарным оператором или шаблонной строкой. Классический случай это панель вкладок, которая подсвечивает выбранную вкладку.
Нажмите Lessons, и подчёркивание переместится, потому что теперь только строка этой кнопки содержит active. Reviews получает класс disabled и атрибут disabled, поэтому она серая и игнорирует клики. Шаблонная строка оставляет лишние пробелы, когда условие ложно, но браузеры их игнорируют. Когда у компонента много условных классов, массив аккуратнее:
const classes = ['tab', isActive && 'active', isDisabled && 'disabled']
.filter(Boolean)
.join(' ');
Многие проекты используют для той же задачи крошечный пакет clsx: clsx('tab', { active: isActive, disabled: isDisabled }).
Проп style
Проп style принимает объект JavaScript, а не строку. Поэтому у него двойные скобки: внешняя пара открывает выражение JavaScript, внутренняя это литерал объекта. Имена свойств пишутся в camelCase (backgroundColor, fontSize, borderRadius), а значения это строки или числа. Число у свойства длины становится пикселями, поэтому padding: 12 означает 12px.
Inline-стили хороши, когда значение вычисляется во время рендера, например ширина индикатора прогресса.
Нажимайте Add 10%, пока полоса не заполнится, и она станет зелёной. Обратите внимание на width: percent + '%': голое число означало бы пиксели, поэтому единицы, отличные от px, пишутся в строке. Свойства без единиц остаются простыми числами, например opacity: 0.8, zIndex: 2, flexGrow: 1 и lineHeight: 1.5 (это множитель, а не пиксели).
У inline-стилей есть ограничения. Они не могут выразить :hover, :focus, медиазапросы или анимации keyframes, и каждый элемент несёт свою копию правил. Используйте их для динамических значений, а остальное держите в CSS.
CSS-переменные из пропсов
Хорошая золотая середина: задать пользовательское свойство CSS в style и использовать его в таблице стилей. Правила остаются в CSS, включая состояния при наведении, и только значение приходит из JavaScript.
Выберите новый цвет, и обе кнопки последуют за ним, а заливка при наведении всё ещё работает, потому что это обычный CSS. Имена пользовательских свойств в объекте style сохраняют дефисы, поэтому пишутся в кавычках: { '--accent': accent }.
Файлы CSS
В настоящем проекте стили пишут в файлах .css и импортируют из компонента, которому они нужны. С Vite достаточно импорта: инструмент сборки видит import './Button.css', добавляет таблицу стилей на страницу во время разработки и упаковывает её в файл CSS для продакшена. Экспортировать ничего не нужно; импорт существует только ради побочного эффекта.
// Button.jsx
import './Button.css';
export default function Button({ children }) {
return <button className="button">{children}</button>;
}
/* Button.css */
.button {
background: #2563eb;
color: white;
border-radius: 6px;
}
Подвох в том, что каждая импортированная таблица стилей глобальна. Правило .button в Button.css стилизует каждый элемент с этим классом в любом месте приложения, а два файла, которые оба задают .title, перезаписывают друг друга. Команды решают это соглашениями об именовании вроде BEM (.card__title) или через CSS Modules.
CSS Modules
Файл, имя которого заканчивается на .module.css, это CSS Module. Импорт даёт объект, который сопоставляет каждому написанному вами имени класса уникальное сгенерированное имя, поэтому класс применяется только там, где вы его используете. Vite и Next.js поддерживают это без настройки.
// Card.jsx
import styles from './Card.module.css';
export default function Card({ title, children, featured }) {
return (
<section className={featured ? `${styles.card} ${styles.featured}` : styles.card}>
<h3 className={styles.title}>{title}</h3>
{children}
</section>
);
}
/* Card.module.css */
.card { border: 1px solid #ddd; border-radius: 10px; padding: 16px; }
.featured { border-color: #2563eb; }
.title { margin: 0; }
В браузере класс выглядит примерно как _card_1x2ab_1. Другой компонент может задать свой .title, и они никогда не столкнутся. Вы пишете обычный CSS, включая правила наведения и медиазапросы, поэтому CSS Modules часто становятся выбором по умолчанию для стилей компонентов.
Tailwind CSS с React
Tailwind использует другой подход: вместо написания правил вы собираете небольшие утилитарные классы прямо в className. px-4 задаёт горизонтальный отступ, rounded-lg скругляет углы, hover:bg-blue-700 меняет фон при наведении. Tailwind сканирует ваши файлы и генерирует CSS только для используемых классов.
Настройка в приложении React на Vite (Tailwind v4) занимает три шага:
npm install tailwindcss @tailwindcss/vite
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
plugins: [react(), tailwindcss()],
});
/* src/index.css, imported from main.jsx */
@import "tailwindcss";
Затем стилизуйте компоненты классами:
function UpgradeCard({ active }) {
return (
<div className="max-w-xs rounded-xl border border-gray-200 p-4">
<h3 className="mb-2 text-lg font-semibold">Pro plan</h3>
<button
className={`rounded-md px-4 py-2 text-white ${active ? 'bg-green-600' : 'bg-blue-600 hover:bg-blue-700'}`}
>
{active ? 'Active' : 'Upgrade'}
</button>
</div>
);
}
Tailwind недоступен в редакторах на этой странице, поэтому он показан статическим кодом. Условные классы работают точно как в примере с вкладками выше, но пишите каждое имя класса целиком: Tailwind находит классы, сканируя исходный текст, поэтому имя, собранное во время выполнения, например `bg-${color}-600`, никогда не будет сгенерировано.
Когда что использовать
- Обычные файлы CSS подходят для небольших приложений и глобальных стилей (сброс, шрифты, макет страницы). По мере роста приложения следите за конфликтами имён классов.
- CSS Modules подходят для большинства стилей компонентов: обычный CSS, автоматическая изоляция, без дополнительной библиотеки.
- Tailwind подходит командам, которые предпочитают стилизовать в разметке и хотят общую шкалу дизайна для отступов и цветов.
- Проп
styleнужен для значений, вычисленных во время рендера: ширины, позиции, выбранного пользователем цвета. Сочетайте его с CSS-переменными, чтобы правила наведения и медиазапросы оставались в CSS.
Их можно смешивать. В типичном приложении одна глобальная таблица стилей, CSS Modules или Tailwind для компонентов и несколько inline-стилей для динамических значений. Библиотеки CSS-in-JS, такие как styled-components и Emotion, это ещё один вариант, который вы встретите в старых проектах. Страница об установке React показывает, как начать проект на Vite, где всё это работает.
Часто задаваемые вопросы
Почему React использует className вместо class?
JSX компилируется в JavaScript, а там class это зарезервированное слово. React использует имя свойства DOM, className, так же называется и element.className в обычном JavaScript. В HTML оно всё равно становится обычным атрибутом class.
Как писать inline-стили в React?
Передайте объект в проп style с именами свойств в camelCase: style={{ backgroundColor: 'teal', fontSize: 18 }}. Внешние скобки открывают выражение JavaScript, а внутренние это объект. Числа становятся пикселями для свойств, которые принимают длину.
Как добавить класс по условию в React?
Соберите строку в JavaScript: className={isActive ? 'tab active' : 'tab'} или шаблонная строка, например className={`tab ${isActive ? 'active' : ''}`}. Когда условий много, небольшой помощник вроде пакета clsx сохраняет читаемость.
Файлы CSS в React глобальные?
Да. import './App.css' добавляет таблицу стилей на всю страницу, поэтому её имена классов могут конфликтовать с другими файлами. CSS Modules (App.module.css) переименовывают каждый класс в уникальное имя, чтобы он применялся только там, где вы его импортируете.
Как использовать Tailwind с React?
В проекте на Vite установите tailwindcss и @tailwindcss/vite, добавьте плагин в vite.config.js и поставьте @import "tailwindcss"; в начало главного файла CSS. Затем пишите утилитарные классы в className, например className="px-4 py-2 rounded bg-blue-600 text-white".