Иконки нужны современным веб-интерфейсам: упрощают навигацию, поясняют функции, улучшают вид. Но эффективная реализация упирается в масштабируемость, динамическую подгрузку и совместимость с SSR.
Чтобы закрыть эти задачи, мы сделали Nuxt Icon v1 — универсальное решение для проектов Nuxt. Опираясь на проверенные приёмы и добавляя новые, Nuxt Icon сочетает производительность, удобство и гибкость.
В этой статье разбираем сложности рендеринга иконок, эволюцию решений и то, как Nuxt Icon объединяет лучшие стороны для удобства разработчиков.
Почему иконки — это сложно?
На первый взгляд иконки просты: небольшие изображения, которые дополняют интерфейс и подсказки.
![]()
С инженерной точки зрения идеальные иконки должны быть:
- Окрашиваемыми — под темы и цветовые схемы
- Масштабируемыми — чёткими в любом размере и разрешении
- Управляемыми — наборы могут содержать сотни и тысячи иконок
- Эффективно собранными — минимум сетевых запросов
- Оптимально загружаемыми — без вреда производительности и UX
- Динамическими — поддержка подгрузки по запросу или в runtime
![]()
Удовлетворить все эти требования может только продуманное решение с балансом компромиссов. Ниже — эволюция подходов к иконкам и как они закрывают эти задачи.
Эволюция решений для иконок
Со временем разработчики пробовали разные способы рендеринга иконок. Кратко — как развивались эти решения и с какими сложностями сталкивались.
1. Тег <img>: первые шаги
Самый простой вариант — тег <img>. Так делали на заре веба.
Картинки хранятся на сервере, в разметке используется <img> с путём, шириной и высотой. Никакой настройки и рантайм-зависимостей, работает «из коробки» в браузере.
![]()
Минусы: пикселизация, сложно менять цвет, плохое масштабирование. Отдельный файл на иконку — много запросов (особенно в эпоху HTTP/1.1). До загрузки возможна «вспышка» пустых иконок. Код получается громоздким из-за путей. Поэтому на современных сайтах этот способ почти не используют.
2. Веб-шрифты: иконочные шрифты
Следующий шаг — веб-шрифты: иконки как символы шрифта. Шрифты векторные и окрашиваемые, поэтому хорошо подходят под иконки.
Провайдеры наборов иконок собирают их в специальный шрифтовой файл, каждой иконке — свой символ Unicode. К нему идёт CSS, который сопоставляет эти символы с классами иконок.
Плюсы очевидны: просто использовать, можно красить и масштабировать, один запрос загружает все иконки.
![]()
Минусы: большая загрузка шрифта, сложно кастомизировать набор. До загрузки шрифта возможна «вспышка» невидимых иконок — запасных шрифтов нет.
3. Inline SVG: иконки как компоненты
С появлением современных фреймворков переиспользование HTML-элементов упростилось. Иконки стали вставлять как компоненты — прямо инлайновые SVG.
Многие наборы иконок поставляют обёртки под каждый фреймворк. Например, в MDI один общий компонент и данные иконки в props, в Tabler — отдельный компонент на каждую иконку.
SVG по природе окрашиваемы, масштабируемы и сохраняют все возможности формата. Обычно иконки попадают в бандл приложения — лишних запросов нет, SSR и первая отрисовка работают без проблем.
![]()
Минусы: много SVG-элементов в DOM, при большом числе иконок это бьёт по производительности. Растёт размер бандла, для каждой связки «набор + фреймворк» нужна своя интеграция — получается привязка к вендору, сменить набор или фреймворк сложнее.
Несмотря на это, подход распространён: менять набор или фреймворк большинству проектов приходится нечасто.
4. Iconify Runtime: динамический API
Iconify объединил более 200 000 иконок из 100+ коллекций. Runtime-решение подгружает иконки по API — доступ к любой иконке без предварительной сборки.
Удобно для контента от пользователя или любого динамического контента, неизвестного на этапе сборки. Настроить просто, можно использовать даже как CDN без сборки.
![]()
Минусы: зависимость от рантайма — иконки появятся только после загрузки JS и данных. SSR и кэширование (например в PWA) усложняются.
5. Иконки-компоненты по требованию
На базе единого API Iconify и подхода Vite «по требованию» мы сделали unplugin-icons: импорт любой иконки как компонента только когда она нужна.
Как unplugin работает с Vite, webpack и rspack. Есть компиляторы для Vue, React, Svelte и Solid. С Iconify одну и ту же иконку можно использовать в любом фреймворке, привязка к вендору минимальна.
![]()
Плюсы и минусы те же, что у других компонентных решений; зато сборка даёт доступ ко всей коллекции Iconify, а в бандл попадают только используемые иконки. Вопросы DOM и рантайма при этом остаются.
6. Иконки на чистом CSS
При работе над UnoCSS появилась идея встраивать иконки целиком в CSS — так родилось решение Pure CSS Icons.
SVG подставляются как data URL, для отображения достаточно одного класса. При необходимости иконки можно окрашивать, масштабировать и даже анимировать.
Браузер кэширует CSS, на иконку — один DOM-элемент. Один CSS-файл, без лишних запросов. Чистый CSS — без рантайма, иконки отображаются вместе с интерфейсом и естественно работают с SSR без доработок на сервере.
![]()
Минусы: нельзя как угодно кастомизировать внутренние элементы SVG, иконки собираются на этапе сборки — без динамики.
Что нужно для интеграции иконок в Nuxt
Pure CSS Icons и иконки-компоненты по требованию в большинстве случаев достаточно для статики. Nuxt как полноценный фреймворк предъявляет дополнительные требования:
- SSR/CSR: Nuxt работает и в SSR, и в CSR. Важен опыт пользователя — иконки должны отображаться сразу, без «вспышек».
- Динамические иконки: в связках вроде Nuxt Content контент может приходить в рантайме или извне и быть неизвестен при сборке. Нужна возможность хорошо интегрироваться с такими сценариями.
- Производительность: иконки должны эффективно попадать в бандл, загрузка — оптимизирована.
- Свои иконки: помимо большого выбора в Iconify, у проектов часто есть свои наборы или платные иконки вне Iconify. Поддержка кастомных иконок важна.
![]()
С учётом этих требований посмотрим на рассмотренные решения.
Для динамических иконок подходит Iconify Runtime: подгрузка по запросу, контент может быть неизвестен при сборке. Минусы: зависимость от рантайма, неудобная интеграция с SSR, кастомные иконки недоступны — запросы идут на серверы Iconify, у которых нет доступа к локальным наборам.
Pure CSS Icons дают отличную производительность и SSR: иконки сразу в разметке, без мерцания, эффективный бандл. Но для динамических иконок не подходят — нужна сборка, подстройка под контент в рантайме ограничена.
Баланс выдержать сложно. Идея Nuxt Icon v1 — объединить сильные стороны обоих подходов.
Nuxt Icon v1: лучшее из двух подходов
Модульная система Nuxt позволила объединить мгновенный рендеринг CSS-иконок и динамическую подгрузку через Iconify. В итоге — гибкое и современное решение, которое подстраивается под нужды проекта.
Два режима рендеринга
Компонент <Icon> поддерживает режимы CSS и SVG, оба дружественны к SSR. В зависимости от задач можно выбирать режим для каждой иконки.
В режиме CSS иконки попадают в CSS при SSR и отображаются сразу, без затрат в рантайме. В режиме SVG они инлайнятся в HTML при SSR с тем же эффектом. В обоих случаях иконки видны на первом кадре без задержки.
![]()
Icon Bundles
Динамические иконки сложнее всего загружать эффективно. Мы используем API Iconify — любая иконка доступна по запросу. Но полагаться только на API значит добавлять задержки, особенно если серверы далеко от пользователя.
Поэтому введены Icon Bundles. Часто используемые иконки можно положить в Client Bundle — они отображаются сразу, без сетевых запросов. Тащить все возможные иконки в клиентский бандл нельзя из-за размера.
Nuxt — full-stack фреймворк, поэтому добавлен Server Bundle. На сервере размер бандла менее критичен, можно включить больше иконок. При SSR они быстро подставляются и уходят на клиент. В итоге частые иконки работают максимально быстро, а при необходимости можно подтянуть любую иконку из Iconify как запасной вариант.
Клиентский бандл для статики и серверный для динамики дают баланс производительности и гибкости.
![]()
Поток данных
Как Nuxt Icon запрашивает данные иконок:
- Используется компонент
<Icon>с указаниемnameиконки. - Nuxt Icon сначала проверяет
Client Bundleи SSR payload (иконки, известные при SSR, уже в payload). Если иконка есть — рендер сразу. - Если на клиенте иконки нет, данные запрашиваются с серверного API вашего Nuxt-приложения. На сервере проверяется
Server Bundle. - Задействованы несколько уровней кэша: кэш серверного endpoint, HTTP-кэш и клиентский кэш, чтобы иконки подгружались быстро. Данные иконок меняются редко, поэтому используются жёсткие стратегии кэширования.
- Если иконка неизвестна и клиенту, и серверу (динамические иконки), серверный endpoint обращается к API Iconify. Endpoint кэшируется — для каждой уникальной иконки API вызывается один раз независимо от числа клиентов.
![]()
Такой многоуровневый подход даёт быструю доставку иконок, баланс скорости и гибкости и по возможности динамику, сглаживая компромиссы между разными решениями.
Попробуйте Nuxt Icon
Nuxt Icon v1 объединяет многолетние наработки в рендеринге иконок. Динамическое приложение, статичный сайт или что-то между — Nuxt Icon подстроится под задачу.
Добавить Nuxt Icon в проект можно одной командой:
npx nuxi module add icon
В компонентах Vue используйте <Icon> с полем name по соглашениям Iconify:
<template>
<Icon name="i-lucide-activity" />
</template>
Подробнее — в документации. Попробуйте возможности модуля и поделитесь впечатлениями — нам интересно, как Nuxt Icon изменит ваши проекты.
Happy Nuxting ✨