Web Components в 2026: Shadow DOM, Custom Elements и когда не нужен фреймворк
Web Components существуют с 2014 года, но долго оставались «технологией будущего». В 2026 году ситуация изменилась: все браузеры поддерживают Custom Elements v1, Shadow DOM, Declarative Shadow DOM работает без JS, а Lit сделал DX сопоставимым с React. Разбираемся, когда Web Components — правильный выбор, а когда проще взять фреймворк.
Custom Elements v1: define, lifecycle, attributes
Custom Elements позволяют создавать собственные HTML-теги с инкапсулированной логикой:
class UserCard extends HTMLElement {
static observedAttributes = ['name', 'avatar'];
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.render();
}
attributeChangedCallback(name, oldVal, newVal) {
if (oldVal !== newVal) this.render();
}
render() {
this.shadowRoot.innerHTML = `
<style>
:host { display: flex; align-items: center; gap: 12px; }
img { width: 48px; height: 48px; border-radius: 50%; }
</style>
<img src="${this.getAttribute('avatar')}" alt="" />
<span>${this.getAttribute('name')}</span>
`;
}
}
customElements.define('user-card', UserCard);
Использование — как обычный HTML:
<user-card name="Светлана" avatar="/img/avatar.jpg"></user-card>
Lifecycle callbacks:
connectedCallback— элемент добавлен в DOM (аналог componentDidMount).disconnectedCallback— удалён из DOM (cleanup, отписки).attributeChangedCallback— изменился наблюдаемый атрибут.adoptedCallback— элемент перемещён в другой document.
Shadow DOM: инкапсуляция, slots, ::part
Shadow DOM создаёт изолированное дерево: стили не протекают наружу и не проникают внутрь. Это решает проблему конфликтов CSS в крупных проектах.
Slots — точки вставки контента извне:
class AlertBox extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
.alert { padding: 16px; border-radius: 8px; background: #fef3cd; }
.title { font-weight: bold; margin-bottom: 8px; }
</style>
<div class="alert">
<div class="title"><slot name="title">Внимание</slot></div>
<slot>Содержимое по умолчанию</slot>
</div>
`;
}
}
customElements.define('alert-box', AlertBox);
<alert-box>
<span slot="title">Ошибка</span>
Не удалось сохранить данные.
</alert-box>
::part — стилизация изнутри снаружи без нарушения инкапсуляции:
/* Внутри компонента: */
<button part="btn">Click</button>
/* Снаружи: */
alert-box::part(btn) { background: red; }
Declarative Shadow DOM
Раньше Shadow DOM требовал JS для инициализации. Declarative Shadow DOM (поддержка во всех браузерах с 2024) позволяет описать shadow tree прямо в HTML:
<user-card>
<template shadowrootmode="open">
<style>:host { display: block; }</style>
<slot></slot>
</template>
Контент внутри shadow root
</user-card>
Это критично для SSR: сервер отдаёт готовый Shadow DOM без ожидания JS. Компонент отображается до загрузки скриптов.
Lit vs vanilla: когда нужна обёртка
Vanilla Web Components работают, но boilerplate утомляет: ручной render, строковые шаблоны, отсутствие реактивности. Lit (от Google) добавляет тонкий слой:
import { LitElement, html, css } from 'lit';
class UserCard extends LitElement {
static styles = css`
:host { display: flex; align-items: center; gap: 12px; }
img { width: 48px; border-radius: 50%; }
`;
static properties = {
name: { type: String },
avatar: { type: String },
};
render() {
return html`
<img src=${this.avatar} alt="" />
<span>${this.name}</span>
`;
}
}
customElements.define('user-card', UserCard);
Lit добавляет ~5 КБ (gzip) и даёт: реактивные свойства, эффективный DOM-патчинг (lit-html), декораторы, SSR. Для компонентных библиотек и дизайн-систем — оптимальный выбор.
Vanilla подходит для простых компонентов (кнопка, тултип) без сложного стейта. Lit — для всего остального.
Интеграция с React/Vue/Angular
Web Components — стандарт браузера, поэтому работают везде. Но есть нюансы:
- React — исторически плохая поддержка (события через addEventListener, свойства через ref). React 19 наконец добавил нативную поддержку custom elements: свойства передаются как props, события работают через on-атрибуты.
- Vue — отличная поддержка из коробки. Vue сам умеет компилироваться в Web Components через defineCustomElement.
- Angular — Angular Elements оборачивает компоненты в Custom Elements. Работает, но тянет Angular runtime.
Когда Web Components для interop: дизайн-система, которую используют команды на разных фреймворках. Один набор компонентов — работает везде.
Когда не стоит: внутри одного React-приложения Web Components добавляют сложность без выгоды. Используйте обычные React-компоненты.
Вывод
Web Components в 2026 — зрелая технология для конкретных задач: дизайн-системы, виджеты для встраивания, микрофронтенды. Declarative Shadow DOM решил проблему SSR, Lit убрал boilerplate, React 19 починил interop. Но для приложений целиком React/Vue/Svelte по-прежнему удобнее — у них богаче экосистема и лучше DX для сложного стейта.