Iconify, jeden interfejs do wielu zestawów ikon
Iconify to warstwa pośrednia między aplikacją a cudzymi zestawami ikon. Zamiast instalować osobną paczkę dla każdego zestawu, podajesz komponentowi nazwę w formacie prefix:name, a dane ikony przychodzą albo z publicznego API, albo z lokalnego pliku JSON. Wydanie @iconify/json 2.2.519 z 22 sierpnia 2026 roku zawiera 236 zestawów i 334 616 ikon.
Co Iconify właściwie dostarcza
Projekt składa się z trzech warstw, które łatwo pomylić, bo wszystkie noszą tę samą nazwę.
Pierwsza to format danych. Zestaw ikon zapisany jest jako pojedynczy obiekt IconifyJSON z polami prefix, info, lastModified, icons, width i height. W polu icons siedzą same treści elementu <svg> bez otoczki, z kolorami monochromatycznych ikon zamienionymi na currentColor. Dzięki temu zmiana koloru ikony sprowadza się do zmiany koloru tekstu w CSS.
Druga to zbiór danych, czyli repozytorium iconify/icon-sets publikowane jako pakiet @iconify/json. Aktualizowane jest kilka razy w tygodniu, co widać po numeracji: wersja 2.2.517 wyszła 16 sierpnia 2026, 2.2.518 osiemnastego, a 2.2.519 dwudziestego drugiego.
Trzecia to komponenty i narzędzia: @iconify/react, @iconify/vue, @iconify/svelte, web component iconify-icon, wtyczka budowania unplugin-icons, wtyczki do Tailwinda oraz biblioteka @iconify/utils do manipulowania danymi.
Warstwy są rozłączne. Możesz wziąć sam format i wczytywać dane własnym kodem, wziąć same dane i wygenerować z nich pliki SVG, albo wziąć komponent i nie dotykać danych w ogóle. To wyjaśnia, dlaczego pakiety wydawane są niezależnie i dlaczego niektóre stoją w miejscu.
Które pakiety żyją, a które stoją
To pierwsza rzecz do sprawdzenia, bo dokumentacja i poradniki sprzed kilku lat wskazują pakiet, którego dziś brać nie należy.
Pakiet @iconify/iconify ma wersję 3.1.1 opublikowaną 22 czerwca 2023 roku i od tego czasu nic się w nim nie ukazało. Nie jest to porzucenie, tylko zastąpienie, i rejestr npm mówi to wprost. Pole deprecated w metadanych tej wersji zawiera zdanie o tym, że pakiet nie jest już utrzymywany i należy przejść na nowoczesny web component iconify-icon. Jeśli więc trafisz na wpis w konfiguracji budowania, który dokleja @iconify/iconify jako skrypt na stronie, to jest ślad po podejściu z 2023 roku.
Reszta rodziny ma się dobrze. Poniższa tabela zbiera stan na 22 sierpnia 2026 roku, odczytany z rejestru npm.
| Pakiet | Wersja | Data wydania | Uwagi |
|---|---|---|---|
@iconify/iconify | 3.1.1 | 2023-06-22 | oznaczony jako niewspierany, zastąpiony przez iconify-icon |
iconify-icon | 3.0.2 | 2025-10-25 | web component, następca powyższego |
@iconify/react | 6.0.2 | 2025-09-15 | zależność równorzędna react >=16 |
@iconify/vue | 5.0.1 | 2026-05-06 | zależność równorzędna vue >=3.0.0 |
@iconify/svelte | 5.2.2 | 2026-06-11 | zależność równorzędna svelte >5.0.0 |
@iconify/utils | 3.1.4 | 2026-07-05 | funkcje do pracy z danymi |
@iconify/json | 2.2.519 | 2026-08-22 | dane wszystkich zestawów |
@iconify/tools | 5.0.12 | 2026-05-21 | budowanie własnych zestawów |
unplugin-icons | 23.0.1 | 2026-01-14 | osobne repozytorium, projekt unplugin |
@iconify/types | 2.0.0 | 2022-09-08 | tylko typy, stabilne od czterech lat |
Osobno warto sprawdzić zakresy zależności wewnątrz rodziny, bo tu zdarzają się rozjazdy. W tym przypadku ich nie ma. Wszystkie komponenty deklarują @iconify/types w zakresie ^2.0.0, a bieżąca wersja tego pakietu to 2.0.0. @iconify/tools 5.0.12 wymaga @iconify/utils w zakresie ^3.1.3 przy bieżącym 3.1.4, a unplugin-icons 23.0.1 wymaga ^3.1.0. Oba warunki są spełnione.
Data 2022 przy @iconify/types nie oznacza zaniedbania. To pakiet zawierający wyłącznie deklaracje TypeScriptu dla formatu danych, a format się nie zmienił. Inaczej wygląda sprawa wtyczek Tailwinda: @iconify/tailwind 1.2.0 pochodzi z 7 grudnia 2024 roku i obsługuje trzecią wersję Tailwind CSS, natomiast @iconify/tailwind4 1.2.3 z 5 marca 2026 roku obsługuje czwartą. To dwa różne pakiety, nie dwie wersje jednego.
Odpowiedź na pytanie, co wziąć w nowym projekcie w 2026 roku, brzmi więc: komponent dla swojego frameworka albo iconify-icon, dane z @iconify/json lub z pakietów pojedynczych zestawów, a do budowania unplugin-icons. Pakietu @iconify/iconify nie instaluj.
Dwa tryby pracy komponentu: API i offline
To najważniejsza decyzja techniczna przy tym narzędziu i pakiet dla Reacta pokazuje ją najczytelniej, bo ma dwa osobne punkty wejścia zapisane w polu exports.
Domyślny import z @iconify/react to tryb API. Komponent, który dostaje nazwę ikony jako łańcuch znaków, sprawdza pamięć podręczną, a jeśli danych nie ma, wysyła zapytanie HTTP do https://api.iconify.design. Ten punkt wejścia eksportuje funkcje sieciowe: loadIcon, loadIcons, addAPIProvider, iconLoaded, setCustomIconLoader oraz obiekt _api.
Drugi punkt wejścia, @iconify/react/offline, eksportuje tylko cztery rzeczy: Icon, InlineIcon, addCollection i addIcon. Nie ma tam kodu sieciowego w ogóle. Ikona, której nie dodałeś wcześniej przez jedną z tych dwóch funkcji, po prostu się nie wyrenderuje. Sygnatury też się różnią: w trybie API addCollection(data, provider?: string) zwraca wartość logiczną, a w trybie offline addCollection(data, prefix?: string | boolean) nie zwraca nic.
Tak wygląda tryb domyślny, czyli ten, który ładuje dane z cudzego serwera.
// tryb API: każde nowe wyświetlenie ikony to zapytanie HTTP
import { Icon } from '@iconify/react'
export function SaveButton() {
return (
<button type="button">
<Icon icon="mdi:content-save" width={20} height={20} />
Zapisz
</button>
)
}A tak wygląda przejście na offline. Zmieniasz ścieżkę importu i rejestrujesz dane samodzielnie.
// tryb offline: zero ruchu sieciowego, dane w pakiecie aplikacji
import { Icon, addCollection } from '@iconify/react/offline'
import iconsSubset from './icons/subset.json'
addCollection(iconsSubset)
export function SaveButton() {
return (
<button type="button">
<Icon icon="mdi:content-save" width={20} height={20} />
Zapisz
</button>
)
}Plik subset.json trzeba wytworzyć, bo wciągnięcie całego zestawu mdi oznaczałoby dołożenie do pakietu 7447 ikon. Do wycinania podzbioru służy funkcja getIcons z @iconify/utils, a do znalezienia pliku źródłowego lookupCollection z @iconify/json.
// scripts/build-icons.ts, uruchamiane przed budowaniem aplikacji
import { writeFile } from 'node:fs/promises'
import { lookupCollection } from '@iconify/json'
import { getIcons } from '@iconify/utils'
const mdi = await lookupCollection('mdi')
const subset = getIcons(mdi, ['content-save', 'delete-outline', 'pencil'])
if (!subset) {
throw new Error('nie znaleziono żadnej z podanych ikon')
}
await writeFile('./src/icons/subset.json', JSON.stringify(subset))Ta sama para trybów istnieje w pozostałych komponentach. @iconify/vue 5.0.1 ma w polu exports wpis ./offline. @iconify/svelte 5.2.2 wystawia ./dist/OfflineIcon.svelte i ./dist/offline-functions obok wariantów sieciowych.
Argumenty za trybem API są uczciwe: nie musisz nic budować, użytkownik pobiera tylko te ikony, które faktycznie zobaczy, a komponent ma wbudowaną nadmiarowość. Dokumentacja API opisuje ją dokładnie: przy braku odpowiedzi z głównego adresu przez 0,75 sekundy komponent próbuje adresów zapasowych https://api.simplesvg.com i https://api.unisvg.com, z których każdy wskazuje na połowę serwerów.
Argumenty przeciw są równie konkretne. Każde wyświetlenie strony u nowego użytkownika oznacza zapytanie do serwera, którego nie kontrolujesz, wraz z jego adresem IP i nagłówkiem Referer. W projekcie objętym audytem prywatności to osobna pozycja do zadeklarowania. Dostępność ikon zależy od cudzej infrastruktury, a dokumentacja mówi wprost, że serwery są darmowe, ale ich utrzymanie kosztuje, i prosi o wsparcie finansowe. Nie ma tam żadnej umowy o poziomie usług ani płatnego planu z gwarancją. Do tego dochodzi migotanie przy pierwszym renderowaniu, częściowo łagodzone atrybutami ssr i fallback w komponencie dla Reacta.
Trzecia droga to postawienie własnego API. Kod serwera jest otwarty, pakiet @iconify/api ma wersję 3.2.0 z 28 listopada 2025 roku na licencji MIT, a dokumentacja opisuje wdrożenie z repozytorium, z npm i z obrazu kontenera. Komponent kieruje się na własny adres przez addAPIProvider.
import { addAPIProvider, Icon } from '@iconify/react'
addAPIProvider('local', {
resources: ['https://ikony.example.com']
})
// od teraz nazwa z przedrostkiem dostawcy trafia na własny serwer
export const Save = () => <Icon icon="@local:mdi:content-save" />Licencje zestawów ikon, czyli miejsce, gdzie audyt się wykłada
Kod Iconify jest jednolicie otwarty. Plik license.txt w repozytorium iconify/iconify zawiera tekst MIT z notą praw autorskich Vjacheslava Trushkina z lat 2021 do dziś, pole license w npm dla @iconify/react, @iconify/vue, @iconify/svelte, @iconify/utils i @iconify/tools ma wartość MIT, a opublikowana paczka @iconify/react 6.0.2 zawiera plik license.txt z pełnym tekstem MIT. Trzy źródła zgodne, bez niespodzianek.
Z danymi jest zupełnie inaczej i to jest realny problem prawny, a nie formalność.
Pakiet @iconify/json 2.2.519 deklaruje w npm licencję MIT. W jego zawartości nie ma jednak żadnego pliku licencyjnego w katalogu głównym. Jedyny plik z tekstem licencji leży w lib/license.txt i dotyczy pomocniczej klasy Finder dla PHP, z notą praw autorskich z lat 2017 i 2018. Repozytorium iconify/icon-sets również nie ma pliku licencyjnego w katalogu głównym: adresy license.txt, LICENSE i LICENSE.md zwracają kod 404. Pole MIT opisuje więc kod pomocniczy, a nie ikony.
Ikony mają własne licencje, zapisane w polu info.license każdego pliku json/<prefix>.json. Pole ma trzy podpola: title z nazwą czytelną dla człowieka, spdx z identyfikatorem i url z adresem tekstu licencji. Te same dane są zebrane w pliku collections.json oraz w czytelnym collections.md w repozytorium. Rozkład licencji w wydaniu 2.2.519 wygląda tak.
| Grupa licencji | Liczba zestawów | Co to znaczy w praktyce |
|---|---|---|
| MIT | 106 | bez ograniczeń, wymagana nota o prawach autorskich |
| CC BY 4.0 i CC BY 3.0 | 53 | wymagane podanie autorstwa w produkcie |
| Apache 2.0 | 31 | permisywna, z klauzulą patentową |
| Open Font License | 13 | zakaz sprzedaży samych ikon jako pliku |
| CC BY-SA 4.0 i 3.0 | 9 | dzieła pochodne na tej samej licencji |
| CC0 i Unlicense | 11 | domena publiczna |
| GPL 2.0 i GPL 3.0 | 6 | licencja wzajemna, do rozważenia z prawnikiem |
| ISC, MPL 2.0, BSD 3-Clause | 5 | permisywne |
| CC BY-NC 4.0 i CC BY-NC-SA 4.0 | 2 | zakaz użycia komercyjnego |
Suma wynosi 236, czyli tyle, ile zestawów zawiera collections.json.
Konkretny przykład zestawu wymagającego podania autorstwa: fa6-brands, czyli Font Awesome 6 Brands, 495 ikon, licencja CC BY 4.0, autor Dave Gandy. Podobnie solar z 7608 ikonami i selfhst z 7107 ikonami. Jeśli w interfejsie użyjesz jednej ikony marki z fa6-brands, licencja wymaga podania autora i wskazania licencji.
Dwa zestawy są zakazane w produkcie komercyjnym: cbi o nazwie Custom Brand Icons, 1718 ikon na CC BY-NC-SA 4.0, oraz ps o nazwie PrestaShop Icons, 479 ikon na CC BY-NC 4.0. Sześć zestawów jest na licencjach z rodziny GPL, w tym dashicons z 342 ikonami i wordpress z 341 ikonami na GPL-2.0-or-later oraz icomoon-free z 491 ikonami na GPL-3.0-or-later. Ikony to nie kod wykonywalny, więc zasięg wzajemności bywa sporny, ale to jest dokładnie ten rodzaj sporu, którego w projekcie komercyjnym nie chcesz prowadzić.
Sprawdzenie licencji konkretnego zestawu przed użyciem zajmuje kilka linii.
import { lookupCollections } from '@iconify/json'
const collections = await lookupCollections()
const risky = Object.entries(collections)
.filter(([, info]) => /NC|GPL|SA/.test(info.license.spdx ?? ''))
.map(([prefix, info]) => `${prefix}: ${info.license.spdx}`)
console.log(risky.join('\n'))Ten sam odczyt bez instalowania czegokolwiek daje zapytanie GET https://api.iconify.design/collections, które zwraca obiekt z tymi samymi polami license.title, license.spdx i license.url.
Pakiety pojedynczych zestawów zachowują się inaczej niż zbiorczy @iconify/json, i to na korzyść. @iconify-json/mdi 1.2.3 z 20 stycznia 2025 roku deklaruje w npm licencję Apache-2.0, czyli licencję samego zestawu, a nie MIT. To dobra wiadomość dla automatycznego audytu zależności. Zła jest taka, że w zawartości tej paczki nie ma żadnego pliku licencyjnego. Dziewięć plików to index.js, index.mjs, index.d.ts, icons.json, info.json, metadata.json, chars.json, package.json i README.md. Tekst licencji Apache 2.0 musisz dołączyć do produktu sam, sięgając po adres z pola info.license.url.
Na koniec drobiazg, który potrafi zmylić przy przepisywaniu listy licencji: stopka witryny dokumentacyjnej iconify.design mówi o udostępnieniu na licencji Apache 2.0 z prawami autorskimi Iconify OÜ, podczas gdy plik licencyjny repozytorium z kodem to MIT. Stopka dotyczy treści dokumentacji, kod biblioteki jest na MIT.
Wariant kompilowany: unplugin-icons i wtyczki Tailwinda
Trzecie podejście omija komponent uruchomieniowy w całości. unplugin-icons zamienia import wirtualnej ścieżki na gotowy komponent w czasie budowania, więc do przeglądarki trafia sam kod SVG bez biblioteki, która by go szukała.
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import Icons from 'unplugin-icons/vite'
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
export default defineConfig({
plugins: [
react(),
Icons({
compiler: 'jsx',
jsx: 'react',
scale: 1.2,
defaultClass: 'icon',
autoInstall: false,
customCollections: {
marka: FileSystemIconLoader('./src/assets/icons')
}
})
]
})W kodzie aplikacji importujesz ikonę ścieżką ~icons/mdi/content-save, a wtyczka rozwiązuje ją do komponentu. Przedrostek virtual:icons nadal działa w Vite, ale dokumentacja zaleca ~icons dla spójności między narzędziami budowania. Opcja autoInstall dopisuje brakujące pakiety zestawów do projektu i jest w dokumentacji oznaczona jako eksperymentalna, więc na serwerze budującym lepiej ją zostawić wyłączoną.
Wtyczka jest tylko ESM, co jej dokumentacja zaznacza wprost. Zależności równorzędne @svgr/core, @svgx/core, @vue/compiler-sfc i svelte są oznaczone jako opcjonalne w polu peerDependenciesMeta, więc menedżer pakietów nie będzie się dopominał o pakiety dla frameworków, których nie używasz. Przykłady w repozytorium obejmują Vite z Reactem i z Vue 3, Next.js, Nuxt 4, SvelteKit i Astro.
Osobno stoją wtyczki do Tailwinda, które generują klasy CSS renderujące ikonę jako obraz tła lub maskę, bez żadnego elementu w drzewie dokumentu.
Jest tu jedna liczba, którą źródła podają różnie. Rejestr npm raportuje dla @iconify/json 2.2.519 pole unpackedSize równe 461 996 959 bajtów, a rozpakowanie archiwum daje 442 MiB, z czego 441 MiB to sam katalog json. Dokumentacja unplugin-icons w tym samym momencie pisze o mniej więcej 120 MB. Rozbieżność jest ponaddwukrotna, więc planując miejsce w pamięci podręcznej serwera budującego, licz według liczby z rejestru. Do samego pakietu aplikacji trafiają tylko użyte ikony, ale pobranie i rozpakowanie dotyczy całości.
Iconify a Lucide i pojedyncze zestawy
Opisywany osobno Lucide to inny rodzaj narzędzia i porównywanie ich wprost prowadzi na manowce.
| Kryterium | Iconify | Lucide | Pojedynczy pakiet zestawu |
|---|---|---|---|
| Liczba ikon | 334 616 w 236 zestawach | 1776 w jednym zestawie | zależnie od zestawu |
| Spójność wizualna | żadna między zestawami | jeden styl, jedna siatka | w obrębie zestawu |
| Licencja danych | osobna dla każdego zestawu | ISC z osobną notą MIT dla ikon z Feather | jedna, zadeklarowana w npm |
| Domyślne źródło danych | publiczne API przez sieć | pakiet npm, lokalnie | pakiet npm, lokalnie |
| Rozmiar instalacji | 442 MiB przy @iconify/json | pakiet na framework | od kilku do kilkudziesięciu MB |
| Kiedy sięgnąć | wybór ikony nie jest przesądzony, potrzebne logotypy | produkt z jednym stylem interfejsu | znany, jeden zestaw docelowy |
Sensowny wybór wygląda tak. Jeśli budujesz produkt z własnym językiem wizualnym i wiesz, że wystarczy jeden spójny zestaw, weź ten zestaw bezpośrednio. Lucide jest w Iconify obecny jako lucide z 1776 ikonami na ISC, więc niczego przez to nie tracisz, ale też niczego nie zyskujesz poza dodatkową warstwą. Podobnie logotypy technologii, które w wersji uporządkowanej i opisanej licencyjnie znajdziesz w svgl.
Iconify wygrywa w trzech sytuacjach. Pierwsza to edytor lub konfigurator, w którym to użytkownik wybiera ikonę, bo wtedy wyszukiwarka API i dostęp do wszystkich zestawów są sednem funkcji. Druga to panel administracyjny sklejany z wielu źródeł, gdzie i tak potrzebujesz ikon z kilku rodzin naraz, na przykład logotypów marek obok ikon interfejsu. Trzecia to prototyp, w którym nie chcesz jeszcze podejmować decyzji o zestawie.
W projektach opartych o Nuxt dochodzi jeszcze moduł nuxt-icon, a w bibliotekach komponentów pokroju shadcn/ui ikony przychodzą już wybrane wraz z komponentami, więc dokładanie Iconify obok tworzy dwa równoległe systemy ikon w jednej aplikacji.
Typowe błędy
Instalowanie @iconify/iconify na podstawie starszego poradnika. Pakiet stoi od czerwca 2023 roku i ma w npm adnotację o zastąpieniu przez iconify-icon. Wersja 3.1.1 nadal się zainstaluje i nadal będzie działać, ale nie dostanie już poprawek.
Zostawienie trybu API na produkcji bez świadomej decyzji. Import z @iconify/react bez przyrostka /offline oznacza, że każdy użytkownik odpytuje serwer, którego nie kontrolujesz. To bywa akceptowalne, ale musi być wyborem, a nie skutkiem ubocznym skopiowanego przykładu.
Przyjęcie, że skoro @iconify/json jest w npm oznaczony jako MIT, to wszystkie ikony są na MIT. Pole opisuje kod pomocniczy. Ikony mają 19 różnych licencji, w tym dwie zakazujące użycia komercyjnego.
Wciągnięcie do pakietu aplikacji całego zestawu przez import icons from '@iconify-json/mdi/icons.json'. Ten plik ma ponad trzy megabajty i zawiera 7447 ikon. Do trybu offline przygotuj podzbiór przez getIcons.
Pomylenie @iconify/tailwind z @iconify/tailwind4. To nie są wersje jednego pakietu, tylko dwa pakiety pod trzecią i czwartą wersję Tailwinda, wydawane niezależnie.
Poleganie na wyszukiwarce ikon bez sprawdzenia pola info.license wybranego zestawu. Katalog pokazuje licencję przy każdym zestawie, ale nikt tego nie wymusza, a kopiowanie nazwy ikony z wyników wyszukiwania jest szybsze niż czytanie metadanych.
FAQ
Który pakiet Iconify wybrać do nowego projektu w Reacie?
@iconify/react w wersji 6.0.2 albo web component iconify-icon 3.0.2 z opakowaniem @iconify-icon/react 3.0.3. Wybór między nimi zależy od tego, czy chcesz element własny w drzewie dokumentu. Pakietu @iconify/iconify nie instaluj, bo od czerwca 2023 roku ma w npm adnotację o zastąpieniu.
Czy dane ikon są ładowane z sieci domyślnie?
Tak. Import z @iconify/react bez przyrostka używa publicznego API pod adresem https://api.iconify.design. Aby to wyłączyć, importuj z @iconify/react/offline i zarejestruj dane funkcją addCollection albo addIcon. Punkt wejścia offline nie zawiera kodu sieciowego.
Jak sprawdzić licencję konkretnego zestawu?
Odczytaj pole info.license z pliku json/<prefix>.json w repozytorium iconify/icon-sets, użyj funkcji lookupCollections() z pakietu @iconify/json albo wywołaj GET https://api.iconify.design/collections. Wszystkie trzy drogi zwracają title, spdx i url.
Czy mogę użyć każdej ikony z Iconify w produkcie komercyjnym?
Nie. Dwa zestawy z 236 mają licencje zakazujące użycia komercyjnego: cbi na CC BY-NC-SA 4.0 i ps na CC BY-NC 4.0. Kolejnych 53 wymaga podania autorstwa, a sześć jest na licencjach z rodziny GPL.
Ile miejsca zajmuje pełny zbiór danych?
Rejestr npm podaje dla @iconify/json 2.2.519 rozmiar po rozpakowaniu równy 461 996 959 bajtów, a pomiar rozpakowanego katalogu daje 442 MiB. Dokumentacja unplugin-icons mówi o około 120 MB, co jest z tym niezgodne. Do pakietu aplikacji trafiają tylko użyte ikony.
Czy publiczne API ma limity zapytań albo płatny plan?
Dokumentacja nie podaje żadnego limitu ani cennika. Opisuje serwery jako darmowe, prosi o wsparcie finansowe projektu i wskazuje trzy adresy z nadmiarowością. Brak umowy o poziomie usług jest tu istotny: jeśli dostępność ikon jest krytyczna, postaw własne API na pakiecie @iconify/api 3.2.0.