Icons
Koala Icons is our own stroke set, drawn on one 24px grid by one spec. Search it, copy any icon as SVG or JSX, and import it as a plain React component.
Koala Icons#
488 icons in 23 categories, and growing. Search by name, tag or category, and switch to Fill to see the solid weight. Hover an icon and use Copy SVG to paste the master into Figma, Copy JSX for the import and the element, or Download SVG for a file.
488 of 488
Install and import#
Install the koala-icons package and import each icon by name. Every icon is a plain function with no context and no client hooks, so it renders in Server Components with no "use client" boundary. Import optical.css once, in your root layout, for the small optical size.
npm install koala-iconsimport "koala-icons/optical.css"import { Bell, ArrowRight } from "koala-icons"
<Bell />
<ArrowRight className="size-5" />
// One module per icon, for setups that don't tree-shake a barrel
import { Bell } from "koala-icons/icons/bell"Sizes#
An icon is 1em by default, so it follows the text beside it. Set a size with Tailwind's size-*, or the size prop in pixels. Pick it by context: 16 in buttons, 20 in rows led by a text label, 14 in chips. Button, Input and Badge size an unclassed icon for you.
size-3.5
14px
chips and badges
size-4
16px
buttons, inputs, menus
size-5
20px
text-label rows, nav items
size-6
24px
display, empty states
Weights#
Two weights: regular, the outline, and fill, the solid version for selected and status states. Fill is derived from the outline, so the two never drift. Only icons with a body get it; a line glyph like a check or an arrow keeps its outline.
<Star /> {/* regular, the default */}
<Star weight="fill" /> {/* selected, active */}
<Check weight="fill" /> {/* no body: stays an outline */}Optical sizes#
At 16px and below the stroke gets heavier, and icons with fine inner marks swap to a simpler small drawing. It happens by itself, from the size each icon renders at, once optical.css is imported. Without it, the large drawing shows at every size.
Trash
14 · 16 · 20 · 24
House
14 · 16 · 20 · 24
Info
14 · 16 · 20 · 24
Copy
14 · 16 · 20 · 24
Color#
Icons draw in currentColor, so a text-* utility on the icon or any ancestor colors it. Use semantic tokens only, never a raw value, so the icon re-themes across all four themes.
Accessibility#
An icon is decorative by default: aria-hidden="true", because the text beside it already says what it means. An icon-only control names the control, not the icon. An icon that carries meaning on its own takes title or aria-label, which gives it role="img".
// Decorative: the label says it, the icon is skipped
<Button>
Continue <ArrowRight />
</Button>
// Icon-only: name the button, not the icon
<Button iconOnly aria-label="Notifications">
<Bell />
</Button>
// Meaningful on its own: give the icon a name
<CheckCircle title="Verified" className="text-success" />
// Avoid: a label on the icon inside a labelled control is read twice
<Button aria-label="Next step">
<ArrowRight aria-label="Next step" />
</Button>In context#
Button and Badge size an unclassed icon to their own step, so composing needs no size class: 16px in a button, 14px in a badge.
import { ArrowRight, Bell, Check, Plus } from "koala-icons"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
<Button><Plus /> New project</Button>
<Button variant="outline">Continue <ArrowRight /></Button>
<Badge variant="success"><Check /> Deployed</Badge>
<Badge variant="info"><Bell weight="fill" /> 3 new</Badge>