# Next.js Lucide Icons

Next.js Lucide icons from lucide-react, used straight in server component pages: find an icon, import it by name, size and color it, label icon-only buttons.

A small icon tells people what a link or button does before they read its label: a bell for notifications, a printer to print, a trash can to delete. [AdminKit for Next.js](/nextjs/) draws its icons with [Lucide](https://lucide.dev/icons/), an open-source set of 2,015 icons on a 24×24 grid. You get them in the free and PRO versions.

Every icon is a React component from the `lucide-react` package. Import `Bell`, write `<Bell />`, and it renders an inline `<svg>` in the color of the surrounding text.

Icons work in server components, so you don't need `"use client"` to show one. Pages in the App Router are server components: they render to HTML at build time and have no state. `lucide-react` adds `"use client"` to the parts that need it, so you don't have to. Next.js still writes the `<svg>` into the page's HTML. The icons appear before any JavaScript runs, and your page stays a server component with its `metadata` export.

## Example

```html
<div class="d-flex gap-3">
  <i data-lucide="house"></i>
  <i data-lucide="bell"></i>
  <i data-lucide="mail"></i>
  <i data-lucide="message-square"></i>
  <i data-lucide="users"></i>
  <i data-lucide="credit-card"></i>
  <i data-lucide="chart-pie"></i>
  <i data-lucide="settings"></i>
</div>
```
```tsx code title="src/app/(dashboard)/projects/page.tsx"
import {
  Bell,
  ChartPie,
  CreditCard,
  House,
  Mail,
  MessageSquare,
  Settings,
  Users,
} from "lucide-react";
import { pageMetadata } from "@/lib/site";

export const metadata = pageMetadata("/projects");

export default function ProjectsPage() {
  return (
    <div className="d-flex gap-3">
      <House />
      <Bell />
      <Mail />
      <MessageSquare />
      <Users />
      <CreditCard />
      <ChartPie />
      <Settings />
    </div>
  );
}
```

## Find an icon

Look the icon up on [lucide.dev](https://lucide.dev/icons/), or open `/icons-lucide` in the template. That page shows every icon and filters them by name and tag while you type.

The name you find there is in kebab-case. The component is the same name in PascalCase: remove the dashes and capitalize each word.

| Lucide name | Component |
| --- | --- |
| `bell` | `Bell` |
| `chart-pie` | `ChartPie` |
| `circle-check-big` | `CircleCheckBig` |
| `message-square` | `MessageSquare` |

A few older names still work as aliases, so `Home` imports `House`. Use the current names from lucide.dev, as the template does.

## Import only what you use

Import icons one by one, by name:

```tsx
import { Download, Printer, Send } from "lucide-react";
```

Next.js then ships only those icons to the browser. The rest of the package never reaches your JavaScript, which is what tree shaking means.

The package also has an `icons` object that holds every icon. The template imports it in one place: `src/components/pages/icons/lucide/lucide-icons.tsx`, the search list of the Lucide demo page. That component starts with `"use client"` because the search field keeps state. Next.js splits the JavaScript per route, so the full set loads on that page only. Keep to named imports everywhere else.

## Sizes

Every icon gets the `lucide` class, which AdminKit's CSS sets to 18×18 pixels and a stroke width of 2. `lucide-sm` makes it 14 pixels and `lucide-lg` 36 pixels. In a `.btn`, icons are 14 pixels.

```html
<i data-lucide="bell" class="lucide-sm"></i>
<i data-lucide="bell"></i>
<i data-lucide="bell" class="lucide-lg"></i>
```
```tsx code
<Bell className="lucide-sm" />
<Bell />
<Bell className="lucide-lg" />
```

Other sizes take a `style` or a class of your own:

```html
<i data-lucide="bell" style="width:24px;height:24px;"></i>
<i data-lucide="bell" style="width:48px;height:48px;"></i>
```
```tsx code
<Bell style={{ width: 24, height: 24 }} />
<Bell style={{ width: 48, height: 48 }} />
```

Don't count on the `size` and `strokeWidth` props. They become attributes on the `<svg>`, and the `lucide` class overrides those. Use `style` instead, for example `style={{ strokeWidth: 1.5 }}`.

## Colors

An icon draws in `currentColor`, the text color of its parent. Bootstrap's text color classes give it a color of its own:

```html
<i data-lucide="bell" class="text-primary"></i>
<i data-lucide="bell" class="text-secondary"></i>
<i data-lucide="bell" class="text-success"></i>
<i data-lucide="bell" class="text-danger"></i>
<i data-lucide="bell" class="text-warning"></i>
<i data-lucide="bell" class="text-info"></i>
```
```tsx code
<Bell className="text-primary" />
<Bell className="text-secondary" />
<Bell className="text-success" />
<Bell className="text-danger" />
<Bell className="text-warning" />
<Bell className="text-info" />
```

## Icons next to text

Next to a label, give the icon `align-middle` so it lines up with the text, and `me-1` for a little space. The invoice page does this for its Print, Download PDF and Send invoice buttons.

```html
<button type="button" class="btn btn-primary">
  <i data-lucide="download" class="align-middle me-1"></i> Download PDF
</button>
```
```tsx code
<button type="button" className="btn btn-primary">
  <Download className="align-middle me-1" /> Download PDF
</button>
```

## Icon-only buttons

Without a label, a screen reader has nothing to say about a button. Put an `aria-label` on the button that names the action. You don't have to hide the icon: `lucide-react` gives it `aria-hidden="true"` unless you pass it an `aria-*`, `role` or `title` prop.

```html
<button type="button" class="btn btn-light" aria-label="Edit">
  <i data-lucide="pen" aria-hidden="true"></i>
</button>
<button type="button" class="btn btn-light" aria-label="Delete">
  <i data-lucide="trash" aria-hidden="true"></i>
</button>
```
```tsx code
import { Pen, Trash } from "lucide-react";

<button type="button" className="btn btn-light" aria-label="Edit">
  <Pen />
</button>
<button type="button" className="btn btn-light" aria-label="Delete">
  <Trash />
</button>
```

## Pass an icon as a prop

A stat card or a menu item often shows an icon that its parent chooses. How you pass it depends on what kind of component receives it.

A server component can take the icon component itself, typed as `LucideIcon`. The Analytics dashboard passes `icon={Users}` to its `Stats` cards, which have no `"use client"`:

```tsx
import { DollarSign, type LucideIcon } from "lucide-react";

function StatCard({ icon: Icon, title }: { icon: LucideIcon; title: string }) {
  return (
    <div className="card">
      <div className="card-body">
        <Icon className="align-middle" /> {title}
      </div>
    </div>
  );
}

<StatCard icon={DollarSign} title="Income" />;
```

A client component can't take it from a server component page. A component is a function, and a server component can only pass data that Next.js can send to the browser. Pass the rendered icon instead, typed as `ReactNode`, as the CRM dashboard of AdminKit PRO does with its `CrmStatCard`:

```tsx title="src/app/(dashboard)/dashboard-crm/page.tsx"
<CrmStatCard
  title="Total customers"
  icon={<Users className="align-middle" />}
  stat="customers"
/>
```

Between client components, either way works. The sidebar is a client component and reads its menu from `src/data/navigation.ts`, where each item has an `icon` such as `SlidersVertical`.

## Accessibility

- `lucide-react` adds `aria-hidden="true"` to an icon, unless you give it an `aria-*`, `role` or `title` prop.
- A button or link with only an icon needs an `aria-label`. The edit and delete links of the tables demo have one.
- If an icon means something on its own, without text next to it, add a text alternative: an `aria-label` on the element around it, or a `<span class="visually-hidden">` with the text.

## Related

- [React Lucide icons](/react/docs/icons/lucide-icons/): the same icons in AdminKit for React (Vite)
- [Bootstrap 5 Lucide icons](/bootstrap/docs/icons/lucide-icons/): the icons of the HTML version
- [Font Awesome icons](/nextjs/docs/icons/font-awesome/): the second icon set of AdminKit PRO
- [Buttons](/nextjs/docs/components/buttons/): buttons with icons
- [Lucide icons demo](https://nextjs-demo.adminkit.io/icons-lucide)

---

Canonical HTML page: https://adminkit.io/nextjs/docs/icons/lucide-icons/

Site overview for agents: https://adminkit.io/llms.txt (expanded: https://adminkit.io/llms-full.txt) · Documentation: https://adminkit.io/bootstrap/docs/ · Questions: support@adminkit.io
