npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@brightlocal/icons

v2.0.0

Published

BrightLocal Design System Icons - Lucide defaults and custom icons

Readme

@brightlocal/icons

BrightLocal Design System Icons - Lucide defaults and custom icons

Overview

This package provides a unified icon system for the BrightLocal Design System, combining:

  • All 5000+ Lucide React icons (tree-shakeable)
  • Custom BrightLocal icons
  • Dynamic icon loading for database-driven UI
  • Full TypeScript support

Installation

pnpm add @brightlocal/icons

Usage

1. Direct Import (Recommended)

Best for tree-shaking - only imports the icons you use.

import { Check, Heart, Star } from "@brightlocal/icons";

<Check className="w-4 h-4" />
<Heart size={20} color="red" fill="red" />
<Star size={16} strokeWidth={2} />

2. DynamicIcon Component

For database-driven icon rendering with code-splitting. Icons are loaded dynamically only when used.

import { DynamicIcon } from "@brightlocal/icons";

// From database or CMS
const iconName = user.settings.preferredIcon; // "heart"
<DynamicIcon name={iconName} size={20} />

// Works with custom icons too
<DynamicIcon name="bright-local-logo" size={32} />

// With Suspense fallback (optional)
<React.Suspense fallback={<div className="w-6 h-6 animate-pulse bg-gray-200 rounded" />}>
  <DynamicIcon name={dbIconName} size={16} />
</React.Suspense>

3. Custom BrightLocal Icons

import { BrightLocalLogo } from "@brightlocal/icons";

<BrightLocalLogo className="h-8 w-8 text-blue-600" />;

Component Props

DynamicIcon Component

  • name: Icon name (kebab-case, e.g., "camera", "chevron-down", "bright-local-logo")
  • size: Icon size in pixels (default: 16)
  • color: Icon color (uses currentColor by default)
  • strokeWidth: Stroke width (default: 1.33)
  • All other SVG props

Flag & Social Media Icons

  • size: Icon size in pixels (default: 16)
  • className: CSS classes for styling
  • All standard SVG props

Benefits

  • Tree-shakeable: Only imports icons you use (with direct imports)
  • Type-safe: Full TypeScript support with autocomplete
  • Flexible: Multiple import patterns for different use cases
  • Lazy loading: DynamicIcon splits icons into separate chunks
  • Consistent: Unified API across all icons
  • Optimized: Minimal bundle impact

Adding Custom Icons

Method 1: Using createLucideIcon (Recommended)

Compatible with DynamicIcon and follows Lucide's guidelines (16×16 canvas, 2px strokes).

// src/custom-icons/my-icon.tsx
import { createLucideIcon } from "@brightlocal/icons";

export const MyIcon = createLucideIcon("MyIcon", [
  ["path", { d: "M12 2v20M2 12h20", key: "cross" }],
  ["circle", { cx: "12", cy: "12", r: "3", key: "center" }],
]);

export default MyIcon;

Then add to dynamic imports:

// src/icons/dynamic-icon-imports.ts
const customIconImports = {
  "my-icon": () =>
    import("../custom-icons/my-icon.js").then((m) => ({
      default: m.MyIcon,
    })),
};

Method 2: Plain SVG Component

For icons that don't need dynamic loading.

// src/custom-icons/my-icon.tsx
import * as React from "react";

export const MyIcon = (props: React.SVGProps<SVGSVGElement>) => (
  <svg viewBox="0 0 16 16" fill="none" {...props}>
    <path d="M12 2v20M2 12h20" stroke="currentColor" strokeWidth={2} />
  </svg>
);

Examples

Icon Gallery

import { Check, X, Plus, Minus, Heart, Star } from "@brightlocal/icons";

const icons = [Check, X, Plus, Minus, Heart, Star];

<div className="grid grid-cols-6 gap-4">
  {icons.map((Icon, i) => (
    <Icon key={i} size={16} />
  ))}
</div>;

Database-Driven Icons

import { DynamicIcon } from "@brightlocal/icons";

// Icons stored in database
const features = [
  { id: 1, name: "Fast", icon: "zap" },
  { id: 2, name: "Secure", icon: "shield" },
  { id: 3, name: "Scalable", icon: "trending-up" },
];

<div className="grid grid-cols-3 gap-4">
  {features.map((feature) => (
    <div key={feature.id}>
      <DynamicIcon name={feature.icon} size={32} />
      <h3>{feature.name}</h3>
    </div>
  ))}
</div>;

Loading States

import { Loader2 } from "@brightlocal/icons";

<button disabled>
  <Loader2 className="mr-2 animate-spin" size={16} />
  Loading...
</button>;

Icon Naming Convention

When using DynamicIcon, icon names follow kebab-case format:

  • Lucide icons: Convert PascalCase to kebab-case
    • ChevronDown"chevron-down"
    • AlertCircle"alert-circle"
    • UserPlus"user-plus"
  • Custom icons: Use kebab-case consistently
    • BrightLocalLogo"bright-local-logo"

Performance Considerations

Direct Imports vs DynamicIcon

Use Direct Imports when:

  • Icons are known at build time
  • Maximum tree-shaking is desired
  • You want the smallest possible bundle
import { Camera, Heart } from "@brightlocal/icons";

Use DynamicIcon when:

  • Icon names come from database/API
  • You need code-splitting for large icon sets
  • Building configurable UI components
<DynamicIcon name={iconFromDatabase} />

Bundle Size Impact

  • Direct import: ~1-2KB per icon (tree-shaken)
  • DynamicIcon: ~4KB + icon loaded on demand

Default Icon Properties

All Lucide icons from @brightlocal/icons are wrapped with design system defaults:

| Property | Default | Description | |----------|---------|-------------| | size | 16 | Icon dimensions in pixels | | strokeWidth | 1.33 | Matches Figma design specs | | absoluteStrokeWidth | true | Keeps stroke consistent regardless of icon size |

Default Sizes by Icon Type

| Icon Type | Default Size | Use Case | |-----------|--------------|----------| | Lucide icons | 16px | UI elements, buttons, inputs | | DynamicIcon | 16px | Database-driven icons | | Flag icons | 16px | Country/region indicators | | Social Media icons | 16px | Brand logos, social links |

// Lucide icons default to 16px with 1.33 strokeWidth
<Check />

// Explicit size — stroke stays consistent (absoluteStrokeWidth: true)
<Check size={20} />
<Check size={12} />

TypeScript Support

Full TypeScript support with type inference:

import { DynamicIcon } from "@brightlocal/icons";
import type { DynamicIconProps } from "@brightlocal/icons";

// Props are fully typed
const props: DynamicIconProps = {
  name: "heart",
  size: 16,
  color: "#ff0000",
};

Accessibility

Icons should include proper accessibility attributes:

// Decorative icons (no semantic meaning)
<Check aria-hidden="true" />

// Meaningful icons (provide label)
<Heart aria-label="Like this post" role="img" />

// Interactive icons (use with button/link)
<button aria-label="Close dialog">
  <X size={20} />
</button>

Troubleshooting

Icon not displaying

  1. Check the icon name is correct (case-sensitive for direct imports)
  2. Verify the icon exists in Lucide library
  3. For DynamicIcon, ensure the icon is registered in dynamic-icon-imports.ts

TypeScript errors

Make sure you have the latest types installed:

pnpm add -D @types/react

Bundle size too large

Use direct imports instead of importing everything:

// ❌ Don't do this
import * as Icons from "@brightlocal/icons";

// ✅ Do this
import { Camera, Heart, Star } from "@brightlocal/icons";

Migration from Other Icon Libraries

From React Icons

// Before (react-icons)
import { FaHeart } from "react-icons/fa";
<FaHeart size={16} />;

// After (brightlocal/icons)
import { Heart } from "@brightlocal/icons";
<Heart size={16} />;

From Heroicons

// Before (heroicons)
import { HeartIcon } from "@heroicons/react/16/outline";
<HeartIcon className="h-6 w-6" />;

// After (brightlocal/icons)
import { Heart } from "@brightlocal/icons";
<Heart className="h-6 w-6" />;

Resources

Contributing

To add new custom icons:

  1. Create icon file in src/custom-icons/
  2. Export from src/index.ts
  3. Add to dynamic imports in src/icons/dynamic-icon-imports.ts
  4. Update customIconsData in Storybook documentation
  5. Run build: pnpm build

License

Part of the BrightLocal Design System. See main repository for license details.