Иконочные шрифты против SVG: что выбрать в 2024 году
Иконочный шрифт грузит 200 иконок ради двадцати нужных и ломает вёрстку у людей с дислексией. SVG-спрайты и React-компоненты решают это иначе. Разбираем цену каждого подхода на живой вёрстке.

Классическая ситуация: подключили Font Awesome ради стрелки, лупы и корзины — а в бандл прилетело 60 КБ шрифта с тысячей глифов, которые никто не использует. Плюс иногда вместо иконки появляется пустой прямоугольник или буква, пока шрифт грузится. Это не баг конкретной библиотеки, это цена самого подхода «иконка как символ шрифта». Ниже — четыре способа выводить иконки на сайте и чем каждый расплачивается.
Почему иконочный шрифт вообще работает не так, как хочется
Иконочный шрифт — это обычный .woff2, где вместо букв нарисованы пиктограммы, привязанные к кодовым точкам (часто в области Private Use Area Юникода). Браузер обращается с ними как с текстом: применяет font-size, color, line-height. Отсюда и удобство, и проблемы.
Три системных ограничения, которые не лечатся настройкой:
- FOIT/FOUT. Пока шрифт не загрузился, на месте иконки либо пусто (Flash of Invisible Text), либо запасной символ (Flash of Unstyled Text). На медленном канале пользователь несколько секунд видит квадраты.
- Одноцветность. Глиф красится одним
color. Двухцветную иконку в один шрифтовой символ не положить — нужны хитрости с наложением двух глифов. - Доступность и подмена шрифта. Если у пользователя включён режим замены шрифтов (частая настройка при дислексии) или расширение вроде OpenDyslexic, ваши иконки превращаются в случайные буквы. Скринридер без правильных ARIA-атрибутов зачитает символ из PUA как мусор.
Иконочный шрифт заставляет браузер думать, что стрелка «вниз» — это буква. Всё, что ломает текст, ломает и ваши иконки.
Четыре подхода и чем каждый платит
1. Иконочный шрифт целиком (Font Awesome, Material Icons через CDN)
Подключаете один CSS с CDN, ставите <i class="fa fa-search"></i> — работает мгновенно, верстальщику не нужно трогать сборку. Плата: тянется весь шрифт независимо от того, используете вы 5 иконок или 500. Для Font Awesome Free полный набор шрифтов исторически весит десятки килобайт в woff2 на каждое начертание (solid, regular, brands) — точный размер сверяйте в документации вашей версии, он меняется от релиза к релизу.
2. Инлайновый SVG прямо в разметке
Иконка живёт в HTML как <svg> с путями. Красится через fill и stroke, поддерживает несколько цветов, градиенты, анимацию отдельных частей. Нет отдельного запроса — иконка приезжает вместе со страницей.
<button aria-label="Поиск">
<svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" focusable="false">
<path d="M8 2a6 6 0 1 0 3.7 10.7l4.3 4.3 1.4-1.4-4.3-4.3A6 6 0 0 0 8 2Zm0 2a4 4 0 1 1 0 8 4 4 0 0 1 0-8Z"
fill="currentColor"/>
</svg>
</button>
Ключевая деталь — fill="currentColor": иконка наследует цвет текста родителя, как это делал шрифт. Плата: если одна иконка встречается на странице сто раз, её путь дублируется сто раз в HTML и раздувает документ.
3. SVG-спрайт с <use>
Все иконки складываются в один файл-спрайт с <symbol id="...">, а в разметке вы ссылаетесь на нужный символ. Определение иконки — один раз, использований — сколько угодно.
<!-- один раз где-то в начале body или отдельным файлом -->
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
<symbol id="icon-search" viewBox="0 0 20 20">
<path d="M8 2a6 6 0 1 0 3.7 10.7l4.3 4.3 1.4-1.4-4.3-4.3A6 6 0 0 0 8 2Zm0 2a4 4 0 1 1 0 8 4 4 0 0 1 0-8Z"/>
</symbol>
</svg>
<!-- сколько угодно раз -->
<svg width="20" height="20" aria-hidden="true">
<use href="#icon-search"/>
</svg>
Внешний файл-спрайт (<use href="/sprite.svg#icon-search"/>) кешируется браузером отдельно от HTML — это удобно, но старый синтаксис через xlink:href вам больше не нужен: он объявлен устаревшим, современные браузеры понимают чистый href. Плата спрайта: раскраска частей иконки в разные цвета через <use> работает ограниченно, наследуется в основном currentColor.
4. SVG как компоненты (React, Vue)
В сборках на Vite или webpack плагин превращает .svg-файл в компонент. Пишете <SearchIcon />, а в бандл попадает только импортированное. Дерево-трясение выкидывает неиспользованные иконки автоматически.
// React, импорт SVG как компонента (svgr в связке с Vite/webpack)
import SearchIcon from './icons/search.svg?react';
function SearchButton() {
return (
<button aria-label="Поиск">
<SearchIcon width={20} height={20} aria-hidden="true" />
</button>
);
}
Синтаксис импорта (?react, отдельный лоадер или библиотека) зависит от сборщика — сверяйте в документации вашего плагина, здесь показан вариант для vite-plugin-svgr. Плата: подход завязан на сборку, для сайта без бандлера не подходит.
Сравнение
| Критерий | Иконочный шрифт | Инлайн-SVG | SVG-спрайт | SVG-компоненты |
|---|---|---|---|---|
| Грузит только нужное | Нет, весь набор | Да | Да, один файл | Да, tree-shaking |
| Многоцветность | Нет | Да | Ограниченно | Да |
| FOIT/FOUT | Есть | Нет | Нет | Нет |
| Ломается при замене шрифта | Да | Нет | Нет | Нет |
| Дублирование в HTML | Нет | Да | Нет | Нет |
| Нужна сборка | Нет | Нет | Нет | Да |
| Анимация частей иконки | Нет | Да | Ограниченно | Да |
Когда что выбирать
- Лендинг или сайт без сборки, 5–15 иконок. Инлайн-SVG прямо в шаблоне. Дублирование при таком количестве несущественно, а зависимостей ноль.
- Многостраничник, десятки повторяющихся иконок, есть шаблонизатор. SVG-спрайт с внешним файлом: одно определение, отдельный кеш, чистый HTML.
- SPA на React/Vue. SVG-компоненты через плагин сборщика: типизация, tree-shaking, пропсы вместо CSS-классов.
- Прототип, админка, внутренний инструмент, где скорость важнее веса. Иконочный шрифт всё ещё оправдан: подключил CDN и забыл. Для продакшена с требованиями к доступности и производительности — уже нет.
Что ломается на практике
Типичные грабли при переходе со шрифта на SVG:
- Иконка не красится. В путях прописан жёсткий
fill="#333"из редактора. Замените наfill="currentColor"или уберите атрибут — иначе CSS не перекроет цвет. - Скринридер зачитывает мусор. Декоративной иконке нужен
aria-hidden="true". Иконке-кнопке без текста —aria-labelна кнопке. Не оставляйте иконку без того или другого. - <use> с внешним файлом не показывает иконку. Частая причина — CORS: спрайт с другого домена или CDN может не подхватиться. Держите спрайт на том же origin.
- Иконка обрезана или растянута. Забыли
viewBoxлибо задалиwidth/height, не совпадающие с пропорциями.viewBoxобязателен для корректного масштабирования. - Спрайт раздулся неоптимизированными путями. Прогоните SVG через SVGO — редакторы вроде Figma экспортируют лишние метаданные, комментарии и избыточную точность координат.
Что делать дальше
Если сейчас на проекте иконочный шрифт — не переписывайте всё разом. Начните с аудита: сколько иконок реально используется против того, что грузится. Дальше выберите один целевой подход из таблицы под ваш стек и мигрируйте иконки по мере правок вёрстки. Настройте SVGO в пайплайне сборки, чтобы спрайт не пух. И проверьте результат в браузере с включённой заменой шрифта и через любой скринридер — именно эти два сценария ломает старый подход, ради них миграция и затевается.
Создатель презентаций: Структура и текст каждого слайда: заголовки-утверждения, одна мысль на слайд, заметки докладчику.
Спросить за 150 ₽Источники: SVG: use element — MDN Web Docs, vite-plugin-svgr — GitHub
Частые вопросы
А если у меня старый проект без сборщика — SVG-компоненты не подойдут?
Иконка вставилась через <use>, но не появляется — что смотреть первым?
Насколько инлайн-SVG раздувает страницу по сравнению со шрифтом?
Можно ли покрасить SVG-иконку в два цвета через currentColor?
Иконочный шрифт вообще устарел, его нельзя использовать?
Почему рекомендуют href вместо xlink:href в <use>?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.