Icons

Merid ships no icon set. Every slot that takes an icon accepts any React node, so lucide-react, Heroicons, Phosphor or your own SVGs all work. Lucide's 24px grid and round joins match Merid's geometry best.

bash
npm i lucide-react
Invited
Custom alert icon
Pass any node to icon, or null to hide it.

Sizes

WhereSizeStroke
Button (leadingIcon, trailingIcon), IconButton, SidebarNav.Item16px1.75
Input leading / trailing, Badge, dense table cells14px1.75
EmptyState icon, Alert icon20px1.5

Lucide's default stroke of 2 reads heavier than Merid's 1.5px text and hairlines at 16px; 1.75 matches the weight of medium labels. Keep one set of props in a constant so every icon stays consistent.

Accessibility

  • Icons next to text are decorative: pass aria-hidden. Button and Input already wrap their icon slots in aria-hidden spans, so this is belt and braces.
  • IconButton requires label, which becomes the accessible name (and tooltip text if you wrap it in Tooltip). The icon itself stays hidden.
  • Never use an icon alone to carry meaning in a Badge or table cell; add text or a VisuallyHidden label.

Colour

Icons use currentColor, so they follow the text tone of their slot — muted in inputs, ink in ghost buttons, white on primary. Do not colour icons with the accent unless the whole control is accented.

Your own SVGs

Draw on a 16 or 20 unit grid, stroke currentColor, no fill, round caps. Give the <svg> aria-hidden="true" and focusable="false" (for old Edge).

tsx
export function ProjectIcon(props: React.SVGProps<SVGSVGElement>) {
  return (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false" {...props}>
      <rect x="2.5" y="2.5" width="11" height="11" rx="3" stroke="currentColor" strokeWidth="1.4" />
    </svg>
  );
}