Lucide alternatives for shadcn/ui in 2026, and what breaks when you swap

Checked against shadcn's component source and each set's own README, October 2026.

Guide4 min read

If you want off Lucide in a shadcn/ui project, pick a set drawn on the same 24×24 grid with a 2px stroke. Tabler Icons and Keyline Icons both are, so your components keep their spacing and the change is mostly an import path. Pick Phosphor for several weights of one drawing, and Heroicons for the Tailwind look, if a smaller set will do.

Keyline is this site's own set, so its weak spots are listed here like everyone else's.

Four sets that fit

SetGrid and strokeStylesIconsReact package
Lucide (opens in a new tab)24×24, 2pxOutline2,015lucide-react
Tabler Icons (opens in a new tab)24×24, 2pxOutline, and filled for 1,0546,257@tabler/icons-react
Phosphor (opens in a new tab)Its own gridSix weights, from thin to duotone1,200+@phosphor-icons/react
Heroicons (opens in a new tab)24×24 at 1.5px, plus 20 and 16 solidOutline, solid316@heroicons/react
Keyline Icons24×24, 2pxStroke, two-tone, duotone and fill, each rounded and sharp1,654@keyline-icons/react

Counts are from each project's site or README in October 2026. All five are free for commercial use: Lucide under ISC, the rest under MIT.

Each set's own SVG at its default stroke, used under the licences above, as are the sheets below. Keyline's trash is bin, and Phosphor and Heroicons call search magnifying-glass.
  • Tabler matches the geometry and is by far the biggest. Only 1,054 of its icons have a filled version, so check yours before counting on filled active states. The stroke prop is stroke, not strokeWidth, and names are prefixed: IconBell.
  • Phosphor is the one for weights, picked with a weight prop. Install @phosphor-icons/react, not the old phosphor-react, and import from @phosphor-icons/react/ssr in Server Components. It draws rounder than the rest.
  • Heroicons draws its 16 and 20 pixel solid sets at those sizes rather than scaling them down, which is its best feature. The set is small and closed to new icon requests.
  • Keyline has every icon in four styles, each with rounded or sharp corners: eight versions of every name, none missing. It's young, out of beta since September 2026, and a quarter the size of Tabler, so check coverage first if your product spans niche areas.
stroke
two-tone
duotone
fill
Six of Keyline's icons in its four styles. Each also comes with sharp corners.

People usually leave Lucide for one of three reasons: no filled style to mark a selected item, a product that looks like every other shadcn app, or an icon it doesn't have. Borrowing that one icon from another set is how two stroke weights end up on one screen.

If none of the three is yours, stay. Lucide is good.

The swap is an import path

shadcn copies its components into your repo, so the imports to change live in your own files, components/ui/ included.

grep -rn --exclude-dir=node_modules 'from "lucide-react"' .

Every set changes the import path. Three of them also change the names, and that is most of the work:

import { Search, Settings } from "lucide-react"
import { IconSearch, IconSettings } from "@tabler/icons-react"
import { MagnifyingGlassIcon, GearIcon } from "@phosphor-icons/react"
import { MagnifyingGlassIcon, Cog6ToothIcon } from "@heroicons/react/24/outline"
import { Search, Settings } from "@keyline-icons/react"

Keyline keeps Lucide's names. Every icon shadcn's components import is there under the same name, CheckIcon as well as Check, except the spinner: Loader2Icon is LoaderCircleIcon. In your own code, the rename you'll meet most is the bin: Trash and Trash2 are Bin and Bin2. There's no absoluteStrokeWidth; the stroke scales with the icon.

To own the files instead of adding a dependency, the shadcn CLI copies single icons in as components that import nothing: npx shadcn add @keyline/bell.

shadcn sizes your icons, not you

Icon size is what catches people after a swap, whichever set they pick. shadcn's components size the SVGs inside them with Tailwind classes, and Button and Sidebar use different rules.

Button uses [&_svg:not([class*='size-'])]:size-4. Every SVG inside is 16px unless it carries a size-* class of its own:

<Button>
  <Plus />                     {/* 16px: the button sized it */}
  <Plus size={32} />           {/* still 16px: the class beats the attribute */}
  <Plus className="size-6" />  {/* 24px: the button steps aside */}
</Button>
Rendered in Chromium with Button's own selector. The width that size={32} writes loses to the class, and a size-6 class opts out.

Sidebar menu buttons use [&>svg]:size-4 instead, with no way out for your own class. To change icon size there, edit the class in components/ui/sidebar.tsx. If an icon won't resize after a swap, it's one of these two rules, not the new set.

Both rules land on 16px, so judge a set there, not at the 24 it was drawn on.

On a desktop screen this sheet is at true size. Heroicons here is its 24px outline set scaled down, not the solid sets it draws at 16 and 20.

A selected item needs a second drawing

A selected nav item reads best as the same icon in a heavier style. That only works when the heavy version is the same drawing, and Lucide has no filled style, so its selected item can only change the background. In Keyline each style is its own import:

import { House } from "@keyline-icons/react"
import { House as HouseFill } from "@keyline-icons/react/fill"

function NavIcon({ active }: { active: boolean }) {
  const Icon = active ? HouseFill : House
  return <Icon />
}
The same sidebar on lucide-react and on @keyline-icons/react. Only the second can switch Home to its fill drawing. Lucide icons © Lucide Icons and Contributors, ISC licence.

Phosphor does the same with one prop, weight={active ? "fill" : "regular"}, Heroicons with an import from @heroicons/react/24/solid, and Tabler with IconBell and IconBellFilled, where a filled version exists.

The bell, resting and selected. Tabler's filled bell is one of its 1,054 filled icons.

To choose between two sets, swap the sidebar first. It has both sizing rules and a selected item, so it shows every problem here before the rest of the app moves.

Getting it

Switch from Lucide Every name and prop that differs, and the shadcn registry setup.

Browse the icons Every drawing, all four styles, both corner shapes.

Custom icons A family no set covers, drawn on the same grid as the free ones.