Sprite
Иконки групп interface/system, interface/product, interface/web, services и extensions рендерятся через SVG-спрайт: спрайт монтируется в документ один раз, а каждая иконка ссылается на его символ через <use href="#...">. Так повторяющиеся иконки не дублируют свои <path> в DOM.
Группы flags и logos спрайта не имеют — это обычные inline-SVG-компоненты. Причина: спрайт полагается на наследование currentColor через <use>, а эти группы сохраняют исходные цвета SVG.
Модель рендера — fallback-first
Каждая sprite-иконка самодостаточна и не требует, чтобы спрайт был смонтирован:
- SSR и первый клиентский рендер — всегда инлайн-SVG-fallback (содержимое зашито в компонент). Иконка видна сразу: до гидрации, до загрузки спрайта, без мигания.
- После маунта иконка проверяет наличие своего символа в DOM. Символ есть — переключается на
<use>. Символа нет — подписывается на событие «спрайт смонтирован» и переключается по факту его вставки (актуально дляSpriteFromUrl, который загружает спрайт асинхронно). - Спрайт так и не появился — иконка остаётся на инлайн-fallback. Это штатный режим: без ошибок и предупреждений в консоли, визуально неотличимо от спрайтового рендера.
Событие передаётся через document, а не через состояние модуля — поэтому переключение работает и при нескольких копиях @cloud-ru/ds-icons в бандле (cjs+esm, разные версии в микрофронтах).
Отличить режимы в DevTools: у fallback-иконки внутри <svg> лежит <g> с path, у спрайтовой — <use href="#snack-uikit-…">.
Sprite — контент в бандле
Sprite печатает содержимое спрайта прямо в разметку. Подходит для SPA без SSR и небольших приложений:
import { Sprite, SpriteSystemSVG } from '@cloud-ru/ds-icons/sprite'
import { SearchSVG } from '@cloud-ru/ds-icons/interface/system'
export function App() {
return (
<>
{/* Подключается один раз в корне приложения */}
<Sprite content={SpriteSystemSVG} />
{/* Иконка сама ссылается на символ через <use href='#...'> */}
<SearchSVG size={24} />
</>
)
}
Доступные спрайты (все — из @cloud-ru/ds-icons/sprite): SpriteSystemSVG (он же SpriteSVG), SpriteWebSVG, SpriteProductSVG, SpriteServicesSVG, SpriteExtensionsSVG. Подключайте только те наборы, иконки из которых реально используются.
SpriteFromUrl — кэшируемый спрайт для SSR
Sprite на SSR отдаёт содержимое спрайта в HTML на каждый запрос заново — браузер не может закэшировать его отдельно от страницы. Для приложений, которые сами хостят статику (Next.js, root-приложение), есть SpriteFromUrl: он загружает спрайт как обычный статический файл через fetch после гидрации, вставляет в текущий документ (сохраняя наследование currentColor — в отличие от <use href="external.svg#id">, которое ломает его в части браузеров) и опирается на HTTP-кэш браузера при повторных заходах. Пока спрайт грузится, иконки показывают инлайн-fallback — визуальной просадки нет.
-
Скопировать файлы спрайтов в статическую директорию приложения на этапе сборки:
npx @cloud-ru/ds-icons copy-sprites --out public/sprites --base-url /spritesКоманда кладёт файлы с content-хэшем в имени (
sprite.system.<hash>.symbol.svg) — их можно отдавать сCache-Control: public, max-age=31536000, immutable— и манифестpublic/sprites/manifest.jsonс сопоставлением{ system: '/sprites/sprite.system.<hash>.symbol.svg', … }. -
Подключить по URL из манифеста:
import { SpriteFromUrl } from '@cloud-ru/ds-icons/sprite' import manifest from '../../public/sprites/manifest.json' export function App() { return ( <> <SpriteFromUrl src={manifest.system} /> {/* … остальной рендер */} </> ) }
Сценарии подключения
Next.js-приложение
Sprite-иконки — client-компоненты, но в SSR-HTML попадает инлайн-fallback, поэтому иконки видны до гидрации. Спрайт монтируется один раз в корневом layout (client-часть) — обычно SpriteFromUrl по шагам выше. После гидрации иконки переключаются на <use> самостоятельно.
Root-приложение (контейнер микрофронтов)
Контейнер монтирует спрайт один раз у себя в корне. Дочерним микрофронтам этого достаточно: событие «спрайт смонтирован» идёт через общий document, поэтому иконки из микрофронтов переключаются на <use> даже при собственных копиях @cloud-ru/ds-icons в их бандлах. Владелец контейнера отвечает за монтирование и актуальность спрайта.
Микрофронт (не точка входа)
Ничего подключать не нужно — только импортировать иконки:
- контейнер уже смонтировал спрайт — иконки микрофронта переключатся на
<use>сами; - микрофронт запущен standalone (локальная разработка, тесты) — иконки остаются на инлайн-fallback, без ошибок и предупреждений.
Собственный Sprite/SpriteFromUrl внутри такого микрофронта не монтируется и npx @cloud-ru/ds-icons copy-sprites не запускается — иначе документ получает дубликат символов, которые контейнер уже держит в общем DOM.
Расхождение версий контейнера и микрофронта
Спрайт контейнера собран из его версии @cloud-ru/ds-icons; микрофронт может использовать более новую:
- иконка, которой нет в спрайте контейнера, — её символ не найдётся, иконка остаётся на собственном инлайн-fallback с корректным глифом;
- иконка перерисована (тот же
symbolId, другой глиф) —<use>покажет глиф из спрайта контейнера, то есть старый. Лечится обновлением спрайта в контейнере.
Динамическая иконка по id — SpriteIcon
Для сценариев, где id иконки известен только в рантайме (например, выбран в CMS и приходит из API), есть компонент SpriteIcon — рендерит <use href="#symbolId"> на символ смонтированного спрайта, ничего не добавляя в бандл:
import { SpriteIcon } from '@cloud-ru/ds-icons/sprite'
<SpriteIcon symbolId={card.iconId} size={24} />
symbolId— обычный проп: значение меняется — компонент перерисовывается, фабрик и мемоизации у потребителя нет.- Пока символа нет в DOM (спрайт не смонтирован, id неизвестен спрайту) — рендерится
fallback(ReactNode: скелетон, дефолтная иконка), по умолчанию пустой<svg>правильного размера: лейаут не прыгает. - В отличие от статических
*SVG-компонентов, инлайн-fallback с глифом здесь невозможен — глиф известен только спрайту, поэтому для основного пути рендера спрайт обязан быть смонтирован.
Статические *SVG-компоненты пакета построены на этом же компоненте (через фабрику createSpriteIcon) — поведение единое.
Каталог id — манифест символов
Список валидных symbolId генерируется вместе со спрайтами и доступен в двух формах:
SPRITE_SYMBOL_IDSиз@cloud-ru/ds-icons/sprite— типизированная константа{ system: [...], product: [...], … }с типамиSpriteGroupId/SpriteSymbolId. Для валидации и автокомплита id в коде.sprite.symbols.json— JSON рядом со спрайт-файлами;npx @cloud-ru/ds-icons copy-spritesкопирует его в статическую директорию вместе со спрайтами иmanifest.json. Для внешнего тулинга — например, пикера иконок в CMS: каталог всегда синхронен с фактическим содержимым спрайтов.
Схема id: snack-uikit-<group>-<kebab-name> (snack-uikit-product-accept). Неизвестный/протухший id безопасен — иконка остаётся на fallback.
Props
Sprite
Types
SpriteProps| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
content | string | — | yes | |
data-test-id | string | — | no |
SpriteFromUrl
Types
SpriteFromUrlProps| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
src | string | — | yes | URL файла спрайта (обычно из manifest.json, созданного npx @cloud-ru/ds-icons copy-sprites) |
data-test-id | string | — | no |
SpriteIcon
Types
SpriteIconProps| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
symbolId | string | — | yes | id символа спрайта, на который ссылается <use href="#...">; каталог валидных id — SPRITE_SYMBOL_IDS / sprite.symbols.json |
fallback | ReactNode | — | no | Что рендерить внутри <svg>, пока символа нет в DOM (спрайт не смонтирован или id неизвестен) |
testId | string | — | no | Суффикс data-test-id (итоговый атрибут — icon${testId}); переопределяется явным data-test-id |
size | number | — | no | Размер иконки в px (по умолчанию 24) |
data-test-id | string | — | no |