惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News and Events Feed by Topic
S
Security @ Cisco Blogs
S
Secure Thoughts
Attack and Defense Labs
Attack and Defense Labs
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Hacker News - Newest:
Hacker News - Newest: "LLM"
Recent Commits to openclaw:main
Recent Commits to openclaw:main
H
Hacker News: Front Page
博客园 - 叶小钗
H
Heimdal Security Blog
Microsoft Security Blog
Microsoft Security Blog
Forbes - Security
Forbes - Security
AI
AI
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
T
Troy Hunt's Blog
罗磊的独立博客
Application and Cybersecurity Blog
Application and Cybersecurity Blog
爱范儿
爱范儿
GbyAI
GbyAI
The Last Watchdog
The Last Watchdog
TaoSecurity Blog
TaoSecurity Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
D
DataBreaches.Net
Recent Announcements
Recent Announcements
Schneier on Security
Schneier on Security
C
Cisco Blogs
美团技术团队
D
Docker
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
WordPress大学
WordPress大学
月光博客
月光博客
雷峰网
雷峰网
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
H
Hackread – Cybersecurity News, Data Breaches, AI and More
A
Arctic Wolf
B
Blog RSS Feed
Cisco Talos Blog
Cisco Talos Blog
C
Cybersecurity and Infrastructure Security Agency CISA
V
Vulnerabilities – Threatpost
V2EX - 技术
V2EX - 技术
Y
Y Combinator Blog
N
News and Events Feed by Topic
www.infosecurity-magazine.com
www.infosecurity-magazine.com
W
WeLiveSecurity
Security Archives - TechRepublic
Security Archives - TechRepublic
G
GRAHAM CLULEY
Jina AI
Jina AI
Hugging Face - Blog
Hugging Face - Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
The Hacker News
The Hacker News

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
CSS-in-JS vs CSS Modules: что выбрать в 2026?
flaton · 2026-04-23 · via Все публикации подряд на Хабре

Уровень сложностиСредний

Время на прочтение8 мин

Охват и читатели8

Обзор

В 2026 году фронтенд-разработка продолжает развиваться: появляются новые фреймворки, улучшаются инструменты сборки, растут требования к производительности и пользовательскому опыту.

Разработчики сталкиваются с выбором: использовать CSS Modules или CSS-in-JS решения. Эти подходы дают изоляцию стилей и интеграцию с компонентами, но различаются по реализации и ограничениям.

Выбор системы стилизации влияет на разработку и ключевые метрики: размер бандла, скорость первого рендера, поведение при SSR, удобство отладки и поддержку кода. Неподходящий подход может привести к увеличению объёма JavaScript, проблемам с SSR и усложнению масштабирования.

Данная статья не ставит цель назвать одного победителя. Вместо этого мы сравним основные подходы - CSS Modules и CSS-in-JS: 

  • как они влияют на производительность и размер бандла,  

  • насколько комфортно с ними работать в команде,  

  • как ведут себя при серверном рендеринге,  

  • какие компромиссы неизбежны в каждом случае.

Что такое CSS Modules и CSS-in-JS?

Прежде чем сравнивать производительность или удобство, важно понять, о каком типе стилизации вообще идёт речь. 

Оба подхода, упомянутые в названии, относятся к так называемой scoped-стилизации на уровне компонента - то есть каждый компонент владеет своими стилями, и они не влияют на остальное приложение.

Это отличается от utility-first или глобальных CSS-фреймворков (например, Bootstrap или Tailwind CSS), где стили задаются через универсальные утилитарные классы вроде text-center, bg-blue-500 или p-4. Такие классы глобальны по умолчанию и переиспользуются по всему проекту.

CSS Modules


CSS Modules - это не библиотека, а методология, реализуемая на этапе сборки (например через Webpack или Vite). Основная идея проста: каждый CSS-файл, подключенный к компоненту, обрабатывается так, чтобы все его классы стали локальными по умолчанию. Это решает главную проблему глобального CSS — конфликты имён.

Например, файл Button.module.css с классом .primary на выходе превращается в нечто вроде .Button_primary__xY2z9. React-компонент импортирует эти «хэшированные» имена как объект и использует их в className.

Ключевая особенность: стили остаются обычным CSS, а изоляция достигается без JavaScript-рантайма.

CSS-in-JS

Термин CSS-in-JS часто ассоциируется с библиотекой Styled Components, но на самом деле это широкая парадигма, которая в 2026 году делится на два разных направления:

1. Runtime CSS-in-JS

Styled Components или emotion - здесь стили генерируются в браузере во время выполнения. Каждый компонент динамически создаёт CSS-правила и вставляет их в <style>-теги.

Пример Styled Componetnts

import styled from ‘styled-components’

const Button = styled.button`
  background: ${props => props.primary ? '#007bff' : '#6c757d'};
  color: white;
  border: none;
  padding: 8px 16px;
`;

Такой подход удобен для динамических стилей, но требует рантайм-библиотеки, увеличивает JS-бандл и усложняет SSR. 

2. Zero-runtime CSS-in-JS

Linaria или vanilla-extract - стили пишутся на JavaScript, но полностью компилируются в статические CSS-файлы на этапе сборки. В рантайме остаётся только подключение классов — как в CSS Modules.

Пример vanilla-extract

import { style } from '@vanilla-extract/css';

export const primary = style({
  background: '#007bff',
  color: 'white',
  padding: '8px 16px',
  selectors: {
    '&:hover': { opacity: 0.9 }
  }
});

Здесь вы получаете типобезопасность, темизацию на этапе сборки, динамические классы — и при этом нулевой JS-оверхед.

Тип

Примеры

Где обрабатываются стили?

Рантайм

Runtime CSS-in-JS

Styled Components, Emotion

В браузере

Да

Zero-runtime CSS-in-JS

Linaria, vanilla-extract

На этапе сборки

Нет

Все два подхода в этой статье объединяет одно - стиль принадлежит компоненту. Он не «утекает» и не зависит от глобального состояния CSS. Это критически важно в крупных приложениях, дизайн-системах и библиотеках компонентов - там, где предсказуемость и инкапсуляция важнее скорости прототипирования.

Производительность и размер бандла

Выбор между CSS Modules и CSS-in-JS - напрямую влияет на производительность приложения, опыт пользователя и технический долг. В 2026 году, когда Google учитывает метрики вроде LCP и CLS при ранжировании, даже пара лишних килобайт JavaScript или неоптимальная инъекция стилей могут стоить вам трафика.

Рассмотрим три варианта,  с точки зрения бандла, рендера и совместимости с SSR:

  • CSS Modules

  • Runtime CSS-in-JS (на примере Styled Components / Emotion)

  • Zero-runtime CSS-in-JS (на примере vanilla-extract / Linaria).

CSS Modules: минимализм и предсказуемость

CSS Modules не добавляют никакого JavaScript-рантайма. Стили компилируются в отдельные .css-файлы, которые:

  • подключаются через <link rel="stylesheet">,

  • кэшируются браузером,

  • не блокируют выполнение JS (если загружены асинхронно или критически извлечены).

Особенности

  •  0 КБ JavaScript на стили.

  •  Идеальная совместимость с SSR: стили приходят сразу в HTML.

  •  Легко оптимизировать critical CSS.

  •  Поддержка code splitting «из коробки», при использовании Webpack/Vite

Runtime CSS-in-JS: удобство ценой рантайма

Библиотеки вроде Styled Components или Emotion генерируют CSS в браузере. Это дает невероятную гибкость, но имеет цену:

  • Styled Components весит ~14 КБ (min + gzip), Emotion - чуть легче (~10 КБ)

При SSR без настройки:

  • стили не попадают в HTML,

  • возникает FOUC (вспышка нестилизованного контента),

  • клиенту приходится ждать загрузки JS, чтобы увидеть оформление.

Zero-runtime CSS-in-JS: компиляция как преимущество

Библиотеки вроде vanilla-extract и Linaria предлагают лучшее из обоих миров:

  • синтаксис и типобезопасность JavaScript,

  • нулевой рантайм - стили компилируются в статические .css-файлы.

Например, vanilla-extract: генерирует CSS на этапе сборки, экспортирует только имена классов, не требует установки в dependencies (только devDependencies).

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

  • 0 КБ JavaScript на стили.

  • Полная поддержка SSR и гидратации.

  • TypeScript «понимает» стили - автодополнение, рефакторинг, безопасность.

  • Темизация через compile-time переменные.

Показатель

CSS Modules

Runtime CSS-in-JS

Zero-runtime CSS-in-JS

JS-оверхед

0 КБ

10-14 КБ

0 КБ

Формат стилей

.css-файлы

Inline <style>

.css-файлы

Поддержка SSR

Отличная

Требует настройки

Отличная

Critical CSS 

По умолчанию

Требует настройки

По умолчанию

Влияние на FCP / LCP

Минимальное

Умеренное / высокое

Минимальное

Кэширование стилей

Да

Нет

Да

Рассмотрим некоторые показатели подробнее, например Critical CSS:

CSS Modules и zero-runtime CSS-in-JS: современные сборщики умеют автоматически извлекать critical CSS или подключать стили по чанкам. Runtime CSS-in-JS: все стили генерируются динамически, то есть невозможно заранее знать, какие правила понадобятся и critical CSS приходится извлекать вручную.

Теперь посмотрим на проблему с SSR у runtime css-in-js

Допустим мы создаем стилизованную кнопку. 

const Button = styled.button`
  background: blue;
  color: white;
`;

В браузере при первом рендере Styled Components генерирует CSS-правило, создается уникальный класс (что-то вроде, sc-bdVaJa), который динамически вставляется в <head> через тег <style>.

На сервере (при SSR) та же логика не срабатывает автоматически, потому что: нет DOM, нет <head>, нет механизма «собрать все стили, использованные при рендере».

Поэтому браузер будет рисовать обычную кнопку без стилей и только после загрузки и выполнения всего JavaScript-бандла (включая Styled Components) появляется <style>-тег и кнопка получает заданные стили.

Чтобы исправить эту проблему, Styled Components и Emotion предоставляют специальные API для SSR, но они требуют ручной настройки:

Пример для Styled Components

import { renderToString } from 'react-dom/server';
import { ServerStyleSheet } from 'styled-components';

const sheet = new ServerStyleSheet();
const html = renderToString(sheet.collectStyles(<App />));
const styleTags = sheet.getStyleTags();

const fullHtml = `
  <html>
    <head>${styleTags}</head>
    <body><div id="root">${html}</div></body>
  </html>

Здесь мы заранее генерируем css в виде строки (sheet.getStyleTags()) и вставляем его в <head>

Но такой подход тоже имеет свои нюансы, например в Next.js это будет ломать SSR из коробки, потребуется настройка кастомного Document (_document.js)

Если не производить этой настройки, то получаем проблемы с метриками:

  • FCP (First Contentful Paint): откладывается, пока не загрузится JS и не применятся стили.

  • CLS (Cumulative Layout Shift): скорее всего страница будет “прыгать” после применения стилей

  • SEO: поисковики могут проиндексировать нестилизованный контент, особенно если рендеринг медленный.

React Server Components

После появления React Server Components и App Router в Next.js подход к построению фронтенд-приложений изменился: часть компонентов теперь выполняется на сервере и не попадает в клиентский бандл.

И здесь CSS Modules демонстрируют абсолютную совместимость, в то время как runtime CSS-in-JS сталкиваются с ограничениями.

CSS Modules

CSS-файлы обрабатываются на этапе сборки (Vite/Webpack). Классы, по типу .card превращается в уникальный хэш (например, ProductCard_card_1xY2z). В Server Component импортируется просто строка с этим классом. Никакого React-рантайма, никаких хуков, никакого контекста.

Выходит, что CSS Modules - золотой стандарт для серверных компонентов.

Runtime CSS-in-JS

Runtime CSS-in-JS полагается на React Context для передачи темы и на runtime-инъекцию стилей. Это не будет работать в серверных компонентах, потому что те имеют жесткое ограничение:

  • Нет состояния (useState, useReducer)

  • Нет контекста (React Context не доступен)

  • Нет хуков жизненного цикла (useEffect, useLayoutEffect)

  • Нет событий (onClick, onSubmit)

Обходной путь в таком случае может быть следующим: выносить отдельно стилизованные компоненты и использовать ‘use client’, но стили все равно будут рантаймовыми. 

zero-runtime CSS-in-JS не имеет такой проблемы, поскольку стили статические.

Практические примеры


Рассмотрим немного подробнее использование описанных вариантов стилизации, на примере создания и использования кастомных тем для приложения. 

  1. Runtime CSS-in-JS, на примере styled-components

Темы задаются объектом, с необходимыми цветами, например:

export const lightTheme = {
  colors: {
    primary: '#007bff', 
  }
}

export const darkTheme = {
  colors: {
    primary: '#4d9eff',
  }
}

Тему можно передавать через ThemeProvider, обернув в него приложение, управлять текущей темой можно, например стейт менеджером или обычным useState

import { ThemeProvider } from 'styled-components'

Использовать внутри стилизованных компонентов.

const StyledButton = styled.button`
  background-color: ${({ theme }) => theme.colors.primary};
`

У этой реализации есть одна небольшая проблема — редактор кода не даёт подсказок при написании свойств theme. Для решения этой проблемы, понадобится Typescript - типизировать объекты с темами, и расширить стандартный интерфейс темы собственным. 

Zero-runtime CSS-in-JS, на примере vanilla-extract

Темы также создаются в виде объектов.

export const themeContract = createThemeContract({...})
export const lightTheme = createTheme(themeContract, {
  colors: {
    primary: '#007bff',
  }
})
export const darkTheme = createTheme(themeContract, {
  colors: {
    primary: '#4d9eff',
  }
})

Используются для стилизованных компонентов, через созданный themeContract

export const button = style({
  background: themeContract.colors.primary,
})

Динамическая смена темы происходит через CSS-классы на корневом элементе.

const themeClass = theme === 'light' ? lightTheme : darkTheme

<html lang="ru" className={themeClass}>

 CSS Modules

Посмотрим на реализацию тем для приложения, через data атрибуты.

Для каждой темы создаются соответствующие файлы, в них задаются css переменные, следующим образом: 

:root[data-theme="light"] {
  --color: #007bff;
}

:root[data-theme="dark"] {
  --color: #4d9eff;
}

Созданные переменные можно использовать внутри модулей.

button {
  background-color: var(--color);
}

Для переключения темы создается кастомный ThemeProvider, для хранения темы можно использовать useState, а для переключения необходимо указывать соответствующий data атрибут.

document.documentElement.dataset.theme = 'dark'

Итоги

CSS Modules — простой и стабильный вариант: не добавляют JavaScript и корректно работают с SSR и серверными компонентами.

Zero-runtime CSS-in-JS — позволяют писать стили в JavaScript, но без рантайма: всё компилируется в обычный CSS на этапе сборки.

Runtime CSS-in-JS — дают больше гибкости за счёт генерации стилей в рантайме, но увеличивают размер JavaScript и требуют дополнительной настройки для SSR и RSC.

Выбирайте CSS modules, если 

  1. Вы работаете с React Server Components

  2. Для вас критичны FCP, LCP, CLS и SEO

  3. Вы не хотите добавлять ни байта JavaScript ради стилей

Выбирайте zero-runtime CSS-in-JS, если 

  1. Вы тоже работаете с RSC и хотите типобезопасность

  2. Вам нужна строгая типизация стилей

  3. Вы привыкли к синтаксису CSS-in-JS, но не хотите платить рантаймом

Выбирайте runtime CSS-in-JS, если 

  1. Вы точно не используете RSC

  2. У вас небольшой или средний проект, где простота разработки перевешивает метрики производительности