Icon

A versatile icon component that encapsulates lucide-angular's icons with a consistent API and styling, providing an abstraction layer that facilitates future icon library swapping.

PreviousNext
import { Component } from '@angular/core';
 
import { ZardIconComponent } from '../icon.component';
 
@Component({
  selector: 'z-demo-icon-default',
  standalone: true,
  imports: [ZardIconComponent],
  template: `
    <div class="flex items-center gap-4">
      <z-icon zType="house" />
      <z-icon zType="settings" />
      <z-icon zType="user" />
      <z-icon zType="search" />
      <z-icon zType="bell" />
      <z-icon zType="mail" />
    </div>
  `,
})
export class ZardDemoIconDefaultComponent {}
 

Installation

1

Run the CLI

Use the CLI to add the component to your project.

npx @ngzard/ui add icon
1

Add the component files

Create the component directory structure and add the following files to your project.

icon.component.ts
icon.component.ts
import { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation } from '@angular/core';
import { LucideAngularModule } from 'lucide-angular';
import type { ClassValue } from 'clsx';
 
import { iconVariants, type ZardIconVariants } from './icon.variants';
import { mergeClasses } from '../../shared/utils/utils';
import { ZARD_ICONS, type ZardIcon } from './icons';
 
@Component({
  selector: 'z-icon, [z-icon]',
  standalone: true,
  imports: [LucideAngularModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  template: ` <lucide-angular [img]="icon()" [strokeWidth]="zStrokeWidth()" [absoluteStrokeWidth]="zAbsoluteStrokeWidth()" [class]="classes()" /> `,
  host: {},
})
export class ZardIconComponent {
  readonly zType = input.required<ZardIcon>();
  readonly zSize = input<ZardIconVariants['zSize']>('default');
  readonly zStrokeWidth = input<number>(2);
  readonly zAbsoluteStrokeWidth = input<boolean>(false);
  readonly class = input<ClassValue>('');
 
  protected readonly classes = computed(() => mergeClasses(iconVariants({ zSize: this.zSize() }), this.class()));
 
  protected readonly icon = computed(() => {
    const type = this.zType();
    if (typeof type === 'string') {
      return ZARD_ICONS[type as keyof typeof ZARD_ICONS];
    }
 
    return type;
  });
}
 
icon.variants.ts
icon.variants.ts
import { cva, type VariantProps } from 'class-variance-authority';
 
export const iconVariants = cva('flex items-center justify-center', {
  variants: {
    zSize: {
      sm: 'size-3',
      default: 'size-3.5',
      lg: 'size-4',
      xl: 'size-5',
    },
  },
  defaultVariants: {
    zSize: 'default',
  },
});
 
export type ZardIconVariants = VariantProps<typeof iconVariants>;
 
icons.ts
icons.ts
import {
  Archive,
  ArrowLeft,
  ArrowRight,
  ArrowUp,
  ArrowUpRight,
  BadgeCheck,
  Ban,
  Bell,
  Bold,
  BookOpen,
  BookOpenText,
  Calendar,
  CalendarPlus,
  Check,
  ChevronDown,
  ChevronLeft,
  ChevronRight,
  ChevronsUpDown,
  ChevronUp,
  Circle,
  CircleAlert,
  CircleCheck,
  CircleDollarSign,
  CircleX,
  Clipboard,
  Clock,
  Code,
  CodeXml,
  Copy,
  Ellipsis,
  Eye,
  File,
  FileText,
  Folder,
  FolderCode,
  FolderOpen,
  FolderPlus,
  Heart,
  House,
  Inbox,
  Info,
  Italic,
  Layers,
  Layers2,
  LayoutDashboard,
  Lightbulb,
  LightbulbOff,
  ListFilterPlus,
  LoaderCircle,
  LogOut,
  type LucideIconData,
  Mail,
  Minus,
  Monitor,
  Moon,
  MoveRight,
  Palette,
  PanelLeft,
  Plus,
  Popcorn,
  Puzzle,
  Save,
  Search,
  Settings,
  Shield,
  Smartphone,
  Sparkles,
  SquareLibrary,
  Star,
  Sun,
  Tablet,
  Tag,
  Terminal,
  TextAlignCenter,
  TextAlignEnd,
  TextAlignStart,
  Trash2,
  TriangleAlert,
  Underline,
  User,
  Users,
  X,
  Zap,
} from 'lucide-angular';
 
export const ZARD_ICONS = {
  house: House,
  settings: Settings,
  user: User,
  search: Search,
  bell: Bell,
  mail: Mail,
  calendar: Calendar,
  'log-out': LogOut,
  'panel-left': PanelLeft,
  bold: Bold,
  inbox: Inbox,
  italic: Italic,
  underline: Underline,
  'text-align-center': TextAlignCenter,
  'text-align-end': TextAlignEnd,
  'text-align-start': TextAlignStart,
  check: Check,
  x: X,
  info: Info,
  'triangle-alert': TriangleAlert,
  circle: Circle,
  'circle-alert': CircleAlert,
  'circle-check': CircleCheck,
  'circle-x': CircleX,
  'circle-dollar-sign': CircleDollarSign,
  ban: Ban,
  'chevron-down': ChevronDown,
  'chevron-up': ChevronUp,
  'chevron-left': ChevronLeft,
  'chevron-right': ChevronRight,
  'chevrons-up-down': ChevronsUpDown,
  'move-right': MoveRight,
  'arrow-right': ArrowRight,
  'arrow-up': ArrowUp,
  'arrow-up-right': ArrowUpRight,
  folder: Folder,
  'folder-open': FolderOpen,
  'folder-plus': FolderPlus,
  file: File,
  'file-text': FileText,
  'layout-dashboard': LayoutDashboard,
  'loader-circle': LoaderCircle,
  save: Save,
  copy: Copy,
  eye: Eye,
  ellipsis: Ellipsis,
  terminal: Terminal,
  clipboard: Clipboard,
  moon: Moon,
  sun: Sun,
  lightbulb: Lightbulb,
  'lightbulb-off': LightbulbOff,
  palette: Palette,
  sparkles: Sparkles,
  heart: Heart,
  star: Star,
  zap: Zap,
  popcorn: Popcorn,
  shield: Shield,
  puzzle: Puzzle,
  layers: Layers,
  'layers-2': Layers2,
  'square-library': SquareLibrary,
  code: Code,
  'code-xml': CodeXml,
  'book-open': BookOpen,
  'book-open-text': BookOpenText,
  users: Users,
  monitor: Monitor,
  smartphone: Smartphone,
  tablet: Tablet,
  'badge-check': BadgeCheck,
  'folder-code': FolderCode,
  plus: Plus,
  minus: Minus,
  'arrow-left': ArrowLeft,
  archive: Archive,
  clock: Clock,
  'calendar-plus': CalendarPlus,
  'list-filter-plus': ListFilterPlus,
  trash: Trash2,
  tag: Tag,
} as const satisfies Record<string, LucideIconData>;
 
export declare type ZardIcon = keyof typeof ZARD_ICONS | LucideIconData;
 

Examples

default

import { Component } from '@angular/core';
 
import { ZardIconComponent } from '../icon.component';
 
@Component({
  selector: 'z-demo-icon-default',
  standalone: true,
  imports: [ZardIconComponent],
  template: `
    <div class="flex items-center gap-4">
      <z-icon zType="house" />
      <z-icon zType="settings" />
      <z-icon zType="user" />
      <z-icon zType="search" />
      <z-icon zType="bell" />
      <z-icon zType="mail" />
    </div>
  `,
})
export class ZardDemoIconDefaultComponent {}
 

sizes

Small
Default
Large
Extra Large
import { Component } from '@angular/core';
 
import { ZardIconComponent } from '../icon.component';
 
@Component({
  selector: 'z-demo-icon-sizes',
  standalone: true,
  imports: [ZardIconComponent],
  template: `
    <div class="flex items-center gap-6">
      <div class="flex flex-col items-center gap-2">
        <z-icon zType="house" zSize="sm" />
        <span class="text-muted-foreground text-xs">Small</span>
      </div>
 
      <div class="flex flex-col items-center gap-2">
        <z-icon zType="house" zSize="default" />
        <span class="text-muted-foreground text-xs">Default</span>
      </div>
 
      <div class="flex flex-col items-center gap-2">
        <z-icon zType="house" zSize="lg" />
        <span class="text-muted-foreground text-xs">Large</span>
      </div>
 
      <div class="flex flex-col items-center gap-2">
        <z-icon zType="house" zSize="xl" />
        <span class="text-muted-foreground text-xs">Extra Large</span>
      </div>
    </div>
  `,
})
export class ZardDemoIconSizesComponent {}
 

colors

import { Component } from '@angular/core';
 
import { ZardIconComponent } from '../icon.component';
 
@Component({
  selector: 'z-demo-icon-colors',
  standalone: true,
  imports: [ZardIconComponent],
  template: `
    <div class="flex items-center gap-4">
      <z-icon zType="heart" class="text-destructive" />
      <z-icon zType="circle-check" class="text-green-500" />
      <z-icon zType="triangle-alert" class="text-warning" />
      <z-icon zType="info" class="text-blue-500" />
      <z-icon zType="star" class="text-yellow-500" />
      <z-icon zType="zap" class="text-purple-500" />
    </div>
  `,
})
export class ZardDemoIconColorsComponent {}
 

stroke width

Stroke 1
Stroke 2
Stroke 3
Stroke 4
import { Component } from '@angular/core';
 
import { ZardIconComponent } from '../icon.component';
 
@Component({
  selector: 'z-demo-icon-stroke-width',
  standalone: true,
  imports: [ZardIconComponent],
  template: `
    <div class="flex items-center gap-6">
      <div class="flex flex-col items-center gap-2">
        <z-icon zType="house" [zStrokeWidth]="1" />
        <span class="text-muted-foreground text-xs">Stroke 1</span>
      </div>
 
      <div class="flex flex-col items-center gap-2">
        <z-icon zType="house" [zStrokeWidth]="2" />
        <span class="text-muted-foreground text-xs">Stroke 2</span>
      </div>
 
      <div class="flex flex-col items-center gap-2">
        <z-icon zType="house" [zStrokeWidth]="3" />
        <span class="text-muted-foreground text-xs">Stroke 3</span>
      </div>
 
      <div class="flex flex-col items-center gap-2">
        <z-icon zType="house" [zStrokeWidth]="4" />
        <span class="text-muted-foreground text-xs">Stroke 4</span>
      </div>
    </div>
  `,
})
export class ZardDemoIconStrokeWidthComponent {}
 

searchable

Note: These are only the icons currently used in our documentation.
For the complete icon library, visit lucide.dev/icons.
82 of 82 icons
import { CommonModule } from '@angular/common';
import { Component, computed, signal } from '@angular/core';
 
import { toast } from 'ngx-sonner';
 
import { ZardButtonComponent } from '../../button/button.component';
import { ZardEmptyComponent } from '../../empty/empty.component';
import { ZardInputDirective } from '../../input/input.directive';
import { ZardIconComponent } from '../icon.component';
import { ZARD_ICONS } from '../icons';
 
@Component({
  selector: 'z-demo-icon-searchable',
  standalone: true,
  imports: [CommonModule, ZardIconComponent, ZardInputDirective, ZardButtonComponent, ZardEmptyComponent],
  template: `
    <div class="flex w-full flex-col gap-4">
      <div class="flex flex-col gap-2">
        <div class="relative">
          <input z-input type="text" placeholder="Search icons..." [value]="searchQuery()" (input)="onSearchChange($event)" class="w-full" />
          <z-icon zType="search" class="text-muted-foreground pointer-events-none absolute top-1/2 right-3 -translate-y-1/2" />
        </div>
        <div class="text-muted-foreground text-xs leading-relaxed">
          <strong>Note:</strong> These are only the icons currently used in our documentation.
          <br />
          For the complete icon library, visit
          <a href="https://lucide.dev/icons" target="_blank" rel="noopener noreferrer" class="hover:text-foreground underline transition-colors">lucide.dev/icons.</a>
        </div>
      </div>
 
      <div class="text-muted-foreground text-sm">{{ filteredIcons().length }} of {{ totalIcons }} icons</div>
 
      <div class="grid max-h-[600px] grid-cols-2 gap-4 overflow-y-auto pr-4 sm:grid-cols-3 md:grid-cols-3 lg:grid-cols-4">
        @for (iconName of filteredIcons(); track iconName) {
          <button
            z-button
            zType="outline"
            (click)="copyIconCode(iconName)"
            class="group flex h-auto min-h-[70px] w-full flex-col items-center justify-center gap-2 px-3 py-2"
            [title]="'Click to copy: <z-icon zType=&quot;' + iconName + '&quot; />'"
          >
            <z-icon [zType]="iconName" class="shrink-0 transition-transform group-hover:scale-110" />
            <span class="group-hover:text-foreground w-full text-center text-xs leading-relaxed break-words hyphens-auto transition-colors">
              {{ iconName }}
            </span>
          </button>
        }
      </div>
 
      @if (filteredIcons().length === 0) {
        <z-empty zDescription="No icons found for the given search." />
      }
    </div>
  `,
})
export class ZardDemoIconSearchableComponent {
  readonly searchQuery = signal('');
 
  readonly iconNames = Object.keys(ZARD_ICONS) as Array<keyof typeof ZARD_ICONS>;
  readonly totalIcons = this.iconNames.length;
 
  readonly filteredIcons = computed(() => {
    const query = this.searchQuery().toLowerCase().trim();
    if (!query) {
      return this.iconNames;
    }
 
    return this.iconNames.filter(iconName => iconName.toLowerCase().includes(query));
  });
 
  onSearchChange(event: Event): void {
    const input = event.target as HTMLInputElement;
    this.searchQuery.set(input.value);
  }
 
  async copyIconCode(iconName: string): Promise<void> {
    const code = `<z-icon zType="${iconName}" />`;
 
    try {
      await navigator.clipboard.writeText(code);
 
      toast.success('Icon copied!', {
        description: `<z-icon zType="${iconName}" />`,
        duration: 2000,
      });
    } catch (err) {
      console.error('Failed to copy:', err);
      toast.error('Failed to copy', {
        description: 'Could not copy to clipboard',
        duration: 2000,
      });
    }
  }
}
 

API Reference

z-icon

Property Description Type Default
[class] Additional CSS classes ClassValue ''
[zType] (required) The Lucide icon to display LucideIconData
[zSize] Icon size variant 'sm' | 'default' | 'lg' | 'xl' 'default'
[zStrokeWidth] The stroke width of the icon lines number 2
[zAbsoluteStrokeWidth] Whether to use absolute stroke width (scales with icon size) boolean false