Documentation

Icons

Any icon set on Iconify, pinned to a version and compiled into your app one icon at a time.

Declare a set

An icon set is a dependency, so it is declared like one — by name and version, in whitehall.toml:

[icons]
lucide = "1.2.121"
ic = "1.2.4"

The name is the set's Iconify prefix and the version is its @iconify-json package version, which makes it a lockfile: two machines building the same commit get byte-identical icons.

Draw one

Name the set, then the icon:

<Icon name="lucide:heart" />
<Icon name="ic:baseline-calendar-today" size={20} color="primary" />

Only the icons you name are compiled in. A set is tens of thousands of glyphs; an app that draws six ships six.

The same string works anywhere an icon is named, not just on <Icon><IconButton icon="ic:baseline-search" /> reads the set the same way.

Import a set

A file that draws several icons from one set can import the set and drop the prefix. Icon names become PascalCase:

import $icons.lucide

<Icon name={lucide.Heart} />
<Icon name={lucide.Star} />

Or import single icons, which reads best when a file draws one or two:

import $icons.lucide.Heart
import $icons.ic.BaselineSearch

<Icon name={Heart} />
<Icon name={BaselineSearch} />

Two sets can have an icon of the same name. Per-icon imports are the one form where that collides, and Kotlin's own as resolves it:

import $icons.lucide.Home as LucideHome
import $icons.ic.Home

Names

Iconify names are kebab-case and the identifier is its PascalCase form, so the hyphenated spelling is only valid inside a string:

Iconify nameAs a stringAs an identifier
heart"lucide:heart"lucide.Heart
baseline-calendar-today"ic:baseline-calendar-today"ic.BaselineCalendarToday
calendar-check-2"lucide:calendar-check-2"lucide.CalendarCheck2

PascalCase is not reversible, so two names can want the same identifier — arrow-up-1-0 and arrow-up-10 both want ArrowUp10. When that happens neither gets one, because picking a winner would make lucide.ArrowUp10 mean something you cannot see from where it is written. The string form still reaches both.

A name known only at runtime

An icon chosen by a value the compiler cannot see — a field on a fetched record, a user setting — has to be listed, because nothing in the source says which glyph to compile in:

[icons]
lucide = { version = "1.2.121", include = ["heart", "star", "bookmark"] }
<Icon name={item.icon} />   // "lucide:star", decided at runtime

A name that reaches this and is not in include draws nothing and logs why, since the string does not exist until the app runs. An include entry naming an icon the set does not have is a build error rather than an icon that silently never appears.

Errors

RuleWhen
icon-needs-a-setname="home" — a project can declare several sets, so the name has to say which one
icon-set-not-declaredname="lucide:heart" with no lucide in [icons]
icon-not-in-setThe set is declared but has no icon by that name

Finding a version

Sets are published to npm as @iconify-json/<prefix>:

npm view @iconify-json/lucide version
npm view @iconify-json/ic version

Whitehall fetches that tarball once per version per machine and caches it under ~/.whitehall/icons, so a clean build after the first is offline.

Whitehall's own components — the check inside a <Checkbox>, the chevron on a <Select> — carry their own glyphs and need no [icons] table.