Icon

Local SVG icon resolved by variant and name.

import { Icon } from 'ui-lab-components/icon';
Size32
import { Icon } from 'ui-lab-components/icon';
 
export default function Example() {
  return <Icon id="rocket" size={32} />;
}
 

icon/02-sizes

The size prop takes a number (pixels) or any CSS dimension string, and defaults to 20.

Loading interactive preview
import { Flex } from 'ui-lab-components/flex';
import { Icon } from 'ui-lab-components/icon';
 
const sizes: Array<number | string> = [16, 20, 24, 32, '2.5rem'];
 
export default function Example() {
  return (
    <Flex gap="lg" align-center justify-center wrap className="text-foreground-100">
      {sizes.map((size) => (
        <Flex key={String(size)} direction="column" gap="xs" align-center>
          <Icon id="rocket" size={size} />
          <span className="text-xs text-foreground-400">{String(size)}</span>
        </Flex>
      ))}
    </Flex>
  );
}
 

icon/03-color-inheritance

Icons inherit currentColor from their container, so semantic text tokens — foreground, accent, success, warning, and danger — colour them directly.

Loading interactive preview
import { Flex } from 'ui-lab-components/flex';
import { Icon } from 'ui-lab-components/icon';
 
const tones = [
  { id: 'user', label: 'Foreground', className: 'text-foreground-100' },
  { id: 'sparkle', label: 'Accent', className: 'text-accent-500' },
  { id: 'check-circle', label: 'Success', className: 'text-success-500' },
  { id: 'warning', label: 'Warning', className: 'text-warning-500' },
  { id: 'close-circle', label: 'Danger', className: 'text-danger-500' },
];
 
export default function Example() {
  return (
    <Flex gap="lg" align-center justify-center wrap>
      {tones.map((tone) => (
        <Flex
          key={tone.label}
          direction="column"
          gap="xs"
          align-center
          className={tone.className}
        >
          <Icon id={tone.id} size={24} />
          <span className="text-xs">{tone.label}</span>
        </Flex>
      ))}
    </Flex>
  );
}
 

icon/04-buttons-and-badges

Icons composed into Button and Badge slots. Inside a labeled control the icon stays decorative; the icon-only button carries its own aria-label.

Loading interactive preview
import { Badge } from 'ui-lab-components/badge';
import { Button } from 'ui-lab-components/button';
import { Flex } from 'ui-lab-components/flex';
import { Icon } from 'ui-lab-components/icon';
 
export default function Example() {
  return (
    <Flex direction="column" gap="md" align-center justify-center>
      <Flex gap="sm" align-center justify-center wrap>
        <Button icon={{ left: <Icon id="search" size={14} /> }}>
          Search
        </Button>
        <Button variant="secondary" icon={{ right: <Icon id="right" size={14} /> }}>
          Continue
        </Button>
        <Button
          aria-label="Open settings"
          variant="ghost"
          icon={{ left: <Icon id="gear" size={16} /> }}
        />
      </Flex>
 
      <Flex gap="xs" align-center justify-center wrap>
        <Badge icon={{ left: <Icon id="check-circle" size={10} /> }}>Verified</Badge>
        <Badge icon={{ left: <Icon id="bolt" size={10} /> }}>Fast</Badge>
        <Badge icon={{ right: <Icon id="right" size={10} /> }}>Continue</Badge>
      </Flex>
    </Flex>
  );
}
 

icon/05-inputs-and-navigation

Navigation symbols and input prefix/suffix decorations, where the adjacent text already names the item.

Loading interactive preview
import { Flex } from 'ui-lab-components/flex';
import { Icon } from 'ui-lab-components/icon';
import { Input } from 'ui-lab-components/input';
import { Label } from 'ui-lab-components/label';
 
const navigation = [
  { id: 'home', label: 'Overview', active: true },
  { id: 'trend', label: 'Analytics', active: false },
  { id: 'users', label: 'Members', active: false },
  { id: 'gear', label: 'Settings', active: false },
];
 
export default function Example() {
  return (
    <Flex gap="lg" align-start>
      <nav aria-label="Workspace" className="flex w-40 flex-col gap-1">
        {navigation.map((item) => (
          <a
            key={item.label}
            href="#"
            aria-current={item.active ? 'page' : undefined}
            className={`flex items-center gap-2 rounded-sm px-2 py-1.5 text-sm ${
              item.active
                ? 'bg-background-800 text-foreground-100'
                : 'text-foreground-400'
            }`}
          >
            <Icon id={item.id} size={16} />
            {item.label}
          </a>
        ))}
      </nav>
 
      <div className="flex flex-1 flex-col gap-1.5">
        <Label helperText="Filter the member list by name or email.">Search members</Label>
        <Input
          placeholder="Search…"
          icon={{
            prefix: <Icon id="search" size={14} className="text-foreground-400" />,
            suffix: <Icon id="filter" size={14} className="text-foreground-400" />,
          }}
        />
      </div>
    </Flex>
  );
}
 

icon/06-standalone-label

An empty-state icon that communicates on its own uses label to expose an accessible name instead of staying aria-hidden.

Loading interactive preview
import { Button } from 'ui-lab-components/button';
import { Flex } from 'ui-lab-components/flex';
import { Icon } from 'ui-lab-components/icon';
 
export default function Example() {
  return (
    <Flex direction="column" gap="md" align-center justify-center className="text-center">
      <Icon id="warning" label="Warning" size={32} className="text-warning-500" />
 
      <div className="space-y-1">
        <p className="text-sm text-foreground-100">Payment method expired</p>
        <p className="text-xs text-foreground-400">
          Nothing else on this card names the problem, so the icon carries the meaning and
          needs a label.
        </p>
      </div>
 
      <Button variant="secondary" icon={{ left: <Icon id="card" size={14} /> }}>
        Update billing
      </Button>
    </Flex>
  );
}
 

icon/07-custom-asset-source

Point a single icon at another location with asset-url, or configure the whole tree with IconProvider — including a resolve-source function that maps friendly aliases onto Lumen ids, or points at a CDN or base path.

Loading interactive preview
"use client";
 
import { Flex } from 'ui-lab-components/flex';
import { DEFAULT_ICON_ASSET_BASE_URL, Icon, IconProvider } from 'ui-lab-components/icon';
 
// A resolver can map friendly product-level names onto the Lumen ids that
// actually ship, or point the whole app at a CDN / base path.
const aliases: Record<string, string> = {
  house: 'home',
  'magnifying-glass': 'search',
  alert: 'warning',
};
 
const baseUrl = DEFAULT_ICON_ASSET_BASE_URL;
 
export default function Example() {
  return (
    <Flex direction="column" gap="lg" align-center justify-center className="text-foreground-100">
      <Flex direction="column" gap="xs" align-center>
        <Icon id="rocket" size={24} asset-url={baseUrl} />
        <span className="text-xs text-foreground-400">asset-url</span>
      </Flex>
 
      <IconProvider
        resolve-source={({ variant, id }) => `${baseUrl}/${variant}/${aliases[id] ?? id}.svg`}
      >
        <Flex gap="lg" align-center justify-center wrap>
          {Object.keys(aliases).map((id) => (
            <Flex key={id} direction="column" gap="xs" align-center>
              <Icon id={id} size={24} />
              <span className="text-xs text-foreground-400">{id}</span>
            </Flex>
          ))}
        </Flex>
      </IconProvider>
    </Flex>
  );
}
 

icon/08-status-list

A compact service-status list with a metadata footer — status indicators and small inline labels are the densest everyday use for Icon.

Loading interactive preview
import { Icon } from 'ui-lab-components/icon';
 
const services = [
  { id: 'check-circle', name: 'API', status: 'Operational', tone: 'text-success-500' },
  { id: 'warning', name: 'Webhooks', status: 'Degraded', tone: 'text-warning-500' },
  { id: 'close-circle', name: 'Exports', status: 'Outage', tone: 'text-danger-500' },
  { id: 'clock', name: 'Search index', status: 'Rebuilding', tone: 'text-foreground-400' },
];
 
const meta = [
  { id: 'user', value: 'Ana Sørensen' },
  { id: 'clock', value: 'Updated 4m ago' },
  { id: 'database', value: 'eu-central-1' },
];
 
export default function Example() {
  return (
    <div className="flex flex-col gap-4">
      <div className="flex flex-col gap-0.5">
        {services.map((service) => (
          <div
            key={service.name}
            className="flex items-center justify-between gap-3 rounded-sm px-2 py-1.5 text-sm"
          >
            <span className="flex items-center gap-2 text-foreground-100">
              <Icon id={service.id} size={16} className={service.tone} />
              {service.name}
            </span>
            <span className="text-xs text-foreground-400">{service.status}</span>
          </div>
        ))}
      </div>
 
      <div className="flex flex-wrap items-center gap-x-4 gap-y-1 px-2 text-xs text-foreground-400">
        {meta.map((item) => (
          <span key={item.id} className="flex items-center gap-1.5">
            <Icon id={item.id} size={12} />
            {item.value}
          </span>
        ))}
      </div>
    </div>
  );
}