CodeWorlds
Powrót do kolekcji
Przewodnik15 min czytaniaZespół CodeWorlds

Iconify, jeden interfejs do wielu zestawów ikon

Iconify daje 236 zestawów ikon przez jeden komponent. Które pakiety są martwe, jak wyłączyć ruch do cudzego API i co naprawdę mówią licencje.

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.

PakietWersjaData wydaniaUwagi
@iconify/iconify3.1.12023-06-22oznaczony jako niewspierany, zastąpiony przez iconify-icon
iconify-icon3.0.22025-10-25web component, następca powyższego
@iconify/react6.0.22025-09-15zależność równorzędna react >=16
@iconify/vue5.0.12026-05-06zależność równorzędna vue >=3.0.0
@iconify/svelte5.2.22026-06-11zależność równorzędna svelte >5.0.0
@iconify/utils3.1.42026-07-05funkcje do pracy z danymi
@iconify/json2.2.5192026-08-22dane wszystkich zestawów
@iconify/tools5.0.122026-05-21budowanie własnych zestawów
unplugin-icons23.0.12026-01-14osobne repozytorium, projekt unplugin
@iconify/types2.0.02022-09-08tylko 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.

Code
TypeScript
// 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.

Code
TypeScript
// 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.

TSscripts/build-icons.ts
TypeScript
// 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.

Code
TypeScript
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 licencjiLiczba zestawówCo to znaczy w praktyce
MIT106bez ograniczeń, wymagana nota o prawach autorskich
CC BY 4.0 i CC BY 3.053wymagane podanie autorstwa w produkcie
Apache 2.031permisywna, z klauzulą patentową
Open Font License13zakaz sprzedaży samych ikon jako pliku
CC BY-SA 4.0 i 3.09dzieła pochodne na tej samej licencji
CC0 i Unlicense11domena publiczna
GPL 2.0 i GPL 3.06licencja wzajemna, do rozważenia z prawnikiem
ISC, MPL 2.0, BSD 3-Clause5permisywne
CC BY-NC 4.0 i CC BY-NC-SA 4.02zakaz 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.

Code
TypeScript
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.

TSvite.config.ts
TypeScript
// 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.

KryteriumIconifyLucidePojedynczy pakiet zestawu
Liczba ikon334 616 w 236 zestawach1776 w jednym zestawiezależnie od zestawu
Spójność wizualnażadna między zestawamijeden styl, jedna siatkaw obrębie zestawu
Licencja danychosobna dla każdego zestawuISC z osobną notą MIT dla ikon z Featherjedna, zadeklarowana w npm
Domyślne źródło danychpubliczne API przez siećpakiet npm, lokalniepakiet npm, lokalnie
Rozmiar instalacji442 MiB przy @iconify/jsonpakiet na frameworkod kilku do kilkudziesięciu MB
Kiedy sięgnąćwybór ikony nie jest przesądzony, potrzebne logotypyprodukt z jednym stylem interfejsuznany, 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.

Czytaj dalej

Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie