CSS Modules — это обычный CSS-файл с расширением .module.css, в котором имена классов по умолчанию локальны. Сборщик заменяет .button на уникальный .button_a1b2c, поэтому стили одного компонента не протекают в другой. Файл импортируется в JavaScript, а к классу вы обращаетесь как к свойству объекта.
/* Button.module.css */
.button { background: #7f56d9; color: #fff; }
// Button.jsx
import styles from './Button.module.css';
export default () => <button className={styles.button}>OK</button>;
// В браузере: <button class="Button_button__a1b2c">OK</button>
Коротко о главном, если нужен только вывод:
- Проблема, которую решают CSS Modules — глобальный скоуп классов в обычном CSS: любой
.titleвиден всему проекту и легко переопределяется чужим стилем. - Механика — сборщик (Vite, webpack, Next.js) хеширует имена классов на этапе сборки и отдаёт в JS объект «локальное имя → уникальное имя».
- Подключение — создать файл
*.module.cssи импортировать его; в Vite, Next.js и Create React App это работает из коробки, в чистом webpack нуженcss-loaderс опциейmodules. - Расширения —
composesдля наследования классов,:globalдля точечного выхода в глобальную область,@valueи CSS-переменные для токенов. - Альтернативы — CSS-in-JS, Tailwind, BEM; таблица сравнения и честные минусы ниже.
Разбор актуален на 2026 год: проверено по официальному репозиторию css-modules и документации Vite и Next.js 16.
Что такое CSS Modules и какую проблему они решают
По данным github.com.
Официальное определение из репозитория проекта: «CSS Module — это CSS-файл, в котором все имена классов и анимаций по умолчанию имеют локальную область видимости». Ключевое слово здесь — локальность. В обычном CSS все селекторы живут в одном глобальном пространстве имён. Стоит двум разработчикам объявить .card в разных файлах — и стили начинают перебивать друг друга в зависимости от порядка подключения. Отсюда растут !important, длинные цепочки селекторов и правило «не трогай этот CSS, всё сломается».
CSS Modules убирают саму причину: имя класса, которое вы пишете, — локальное. На сборке оно превращается в уникальный идентификатор, поэтому .card из одного компонента физически не может пересечься с .card из другого. Вы возвращаете себе короткие, осмысленные имена классов и перестаёте бояться коллизий.
Вывод раздела: CSS Modules — это не новый язык и не препроцессор, а соглашение об именовании плюс шаг сборки, который делает классы локальными по умолчанию.

- ПОКАЖЕМ, КАК РАЗВЕРНУТЬ МОДЕЛЬ нейросети DEEPSEEK R1 ПРЯМО НА СВОЁМ КОМПЬЮТЕРЕ
- Где и как применять? Потестируем модель после установки на разных задачах
- Как дообучить модель под себя?
Как работают CSS Modules: хеширование имён
Когда вы импортируете .module.css в JS-модуль, сборщик компилирует файл в формат ICSS (Interoperable CSS) и генерирует объект-словарь. Ключи — имена, которые вы написали в CSS; значения — уникальные строки, которые реально попадут в атрибут class.
/* styles.module.css */
.className { color: green; }
// компонент
import styles from './styles.module.css';
element.innerHTML = '<div class="' + styles.className + '">';
// styles.className === "styles_className__2tLf"
Конкретный вид хеша (например, styles_className__2tLf) задаётся шаблоном localIdentName в настройках css-loader и не важен для вашего кода: он лишь гарантированно уникален и связан с нужными правилами. Вы всегда обращаетесь к классу через объект styles, а не через строку.
Практический нюанс, о который спотыкаются на старте: если обратиться к несуществующему классу — styles.buttton с опечаткой — вы получите undefined без ошибки сборки, и элемент просто останется без стилей. Поэтому опечатки в CSS Modules ловятся глазами или TypeScript-типизацией, а не компилятором.
Синтаксис CSS Modules: import styles и styles.className
Базовый рабочий цикл состоит из трёх шагов: объявить класс в .module.css, импортировать файл дефолтным импортом (обычно под именем styles), подставить styles.className в атрибут className (React) или class.
/* Card.module.css */
.card { padding: 24px; border-radius: 12px; }
.title { font: 600 18px Inter, sans-serif; }
// Card.jsx
import styles from './Card.module.css';
export default function Card() {
return (
<div className={styles.card}>
<h3 className={styles.title}>Заголовок</h3>
</div>
);
}
Несколько классов на одном элементе собираются шаблонной строкой или через удобный хелпер:
<button className={`${styles.button} ${styles.primary}`}>Купить</button>
Если имена классов в CSS написаны через дефис (.apply-color), сборщик по умолчанию оставляет их как есть, и обращаться придётся через скобки: styles['apply-color']. В Vite можно включить camelCase-конвертацию (localsConvention: 'camelCaseOnly') и импортировать классы поимённо: import { applyColor } from './x.module.css'.
Подключение CSS Modules: Vite, webpack, Next.js, CRA

Само расширение .module.css — это триггер: сборщик по нему понимает, что файл нужно обрабатывать как модуль. В большинстве современных инструментов настройка не требуется.
Vite
Файлы, оканчивающиеся на .module.css, распознаются как CSS Modules автоматически. Достаточно импортировать и использовать; поведение тонко настраивается через опцию css.modules в vite.config.
import classes from './example.module.css';
document.getElementById('foo').className = classes.red;
Next.js
Next.js (проверено на версии 16) поддерживает CSS Modules из коробки в директории app: создаёте *.module.css и импортируете в компонент. При этом глобальный CSS в Next.js ограничен — его рекомендуют держать только для действительно глобальных стилей, а компонентное оформление вести через модули.
/* app/blog/blog.module.css */
.blog { padding: 24px; }
// app/blog/page.tsx
import styles from './blog.module.css';
export default function Page() {
return <main className={styles.blog}></main>;
}
Create React App
CRA настроен на поддержку модулей без дополнительной конфигурации: создаёте файл с суффиксом .module.css, и всё работает.
Чистый webpack
Единственный случай, где нужна ручная настройка, — сборка на голом webpack. Здесь модули включает опция modules у css-loader:
module.exports = {
module: {
rules: [{
test: /\.module\.css$/,
use: ['style-loader', { loader: 'css-loader', options: { modules: true } }],
}],
},
};
Вывод раздела: в Vite, Next.js и CRA подключение сводится к расширению файла; ручная настройка лоадера остаётся только для чистого webpack.
composes: наследование классов в CSS Modules
Директива composes — аналог наследования: класс подтягивает правила другого класса, и на элемент навешиваются оба уникальных имени. Работает внутри файла, из другого файла и из глобального класса.
/* внутри одного файла */
.base { padding: 12px; border-radius: 8px; }
.primary { composes: base; background: #7f56d9; color: #fff; }
/* из другого файла */
.primary { composes: base from './shared.module.css'; }
/* из глобального класса */
.primary { composes: btn from global; }
Два ограничения из документации: composes должна идти до остальных правил, и не стоит задавать одно и то же свойство в разных композируемых классах из разных файлов — порядок применения станет непредсказуемым. Внутри одного файла composes часто удобнее и прозрачнее, чем препроцессорный @extend.
:global и :local — выход в глобальную область
Иногда стиль должен остаться глобальным: класс от внешней библиотеки, разметка из стороннего виджета, тема на body. Для этого есть :global — он отключает локализацию для конкретного селектора или блока.
/* один селектор */
:global(.swiper-slide) { opacity: 1; }
/* целый блок */
:global {
.legacy-widget { color: #333; }
}
Обратная директива :local(...) нужна редко — только если вы переключили лоадер в глобальный режим по умолчанию и хотите локализовать отдельные селекторы. В типовой связке с .module.css всё и так локально, поэтому вручную писать :local обычно не приходится.
Переменные и токены: @value и CSS custom properties
CSS Modules умеют экспортировать значения через @value — это переменные времени сборки, похожие на переменные в Sass или Less. Их можно объявить в отдельном файле и импортировать.
/* colors.module.css */
@value brand: #7f56d9;
@value radius: 12px;
/* button.module.css */
@value brand, radius from './colors.module.css';
.button { background: brand; border-radius: radius; }
На практике для тем и токенов сегодня чаще берут нативные CSS-переменные: они работают в рантайме, переключаются без пересборки и не требуют плагинов. @value и :root-переменные спокойно сочетаются в одном проекте.
/* global.css */
:root { --main-color: #7f56d9; }
/* button.module.css */
.button { background-color: var(--main-color); }
Сравнение CSS Modules с альтернативами: обычный CSS, CSS-in-JS, Tailwind, BEM
CSS Modules — не единственный способ изолировать стили. Ниже — как подходы решают одну и ту же задачу «класс не должен протекать за пределы компонента».
| Подход | Изоляция стилей | Где живут стили | Сборка/рантайм | Когда брать |
|---|---|---|---|---|
| Обычный CSS | Нет, глобальный скоуп | .css-файлы | Ничего не нужно | Мелкие сайты, статические страницы |
| CSS Modules | Да, локально по умолчанию | .module.css рядом с компонентом | Хеширование на сборке, ноль рантайма | Компонентные SPA на React/Vue/Svelte |
| CSS-in-JS | Да, через генерацию классов | В JS-файле компонента | Часто рантайм-стоимость | Динамические стили от пропсов и стейта |
| Tailwind | Не нужна: утилиты вместо классов | В разметке (className) | Генерация утилит на сборке | Быстрая вёрстка по дизайн-системе |
| BEM | Дисциплиной именования, вручную | Обычные .css-файлы | Ничего не нужно | Проекты без сборщика или legacy |
Важное различие: BEM решает ту же проблему коллизий, что и CSS Modules, но силой соглашения — вы вручную пишете длинные .card__title--active и надеетесь, что команда не нарушит правило. CSS Modules дают ту же изоляцию автоматически, оставляя короткие имена. При этом BEM-именование внутри модулей никто не запрещает — подходы не конфликтуют.
Плюсы и минусы CSS Modules
Сильные стороны:
- Локальный скоуп из коробки — коллизии имён исчезают как класс проблем.
- Ноль рантайм-стоимости: вся работа делается на сборке, в браузер уходит обычный CSS.
- Это по-прежнему настоящий CSS — работают медиазапросы, псевдоклассы, каскад, любые фичи.
- Явные зависимости: видно, какой компонент какие стили импортирует, мёртвый CSS проще находить.
Слабые стороны, о которых честно стоит знать:
- Опечатка в
styles.xxxне вызывает ошибку — класс молча теряется. Лечится TypeScript-типизацией модулей. - Динамические стили «от значения пропса» неудобны — это территория CSS-in-JS или CSS-переменных.
- Нужен сборщик: без шага сборки CSS Modules не работают в принципе.
- Композиция глубже пары уровней
composesчитается тяжелее, чем плоский набор классов.
Вывод: CSS Modules — прагматичный дефолт для компонентных приложений, где нужна изоляция без рантайм-налога и без ухода от обычного CSS.
Частые вопросы про CSS Modules
Чем CSS Modules отличаются от обычного CSS?
В обычном CSS имена классов глобальны и могут конфликтовать между файлами. В CSS Modules имена по умолчанию локальны: сборщик превращает их в уникальные хеши, поэтому одинаковые имена в разных компонентах не пересекаются.
Нужен ли сборщик для CSS Modules?
Да. CSS Modules — это шаг сборки. В Vite, Next.js и Create React App поддержка встроена, для чистого webpack включается опция modules у css-loader. Без сборщика механизм не работает.
Как подключить CSS Modules в Vite?
Создайте файл с расширением .module.css и импортируйте его: import styles from './x.module.css'. Vite распознаёт такие файлы автоматически, дополнительная настройка не нужна.
Можно ли использовать CSS Modules без React?
Да. Механизм не привязан к React — он работает с Vue, Svelte, Angular и даже с ванильным JS, потому что импорт отдаёт обычный объект «имя класса → строка».
CSS Modules или Tailwind — что выбрать?
Это разные инструменты. Tailwind — утилитарные классы в разметке для быстрой вёрстки по дизайн-системе; CSS Modules — изоляция ваших собственных классов. Их часто совмещают: Tailwind на типовое, модули на сложные компонентные стили.
Как избежать опечаток в именах классов?
Подключите генерацию TypeScript-типов для .module.css (плагины вроде typed-css-modules или встроенная поддержка в сборщике). Тогда обращение к несуществующему классу подсветит редактор ещё до запуска.
- Освой нейросеть Perplexity и узнай, как пользоваться функционалом остальных ИИ в одном
- УЧАСТВОВАТЬ ЗА 0 РУБ.
- Расскажем, как получить подписку
- ПОКАЖЕМ, КАК РАЗВЕРНУТЬ МОДЕЛЬ нейросеть DEEPSEEK R1 ПРЯМО НА СВОЁМ КОМПЬЮТЕРЕ