sketchyicons

Packages

One geometry source, one package per target. Nothing is generated from Lucide twice, which is what keeps a glyph identical whichever package you install it from.

How they relate

The generator runs once, against a specific upstream release, and writes @sketchyicons/data. Everything else is a template over that file. A framework package never sees Lucide, and a bug fixed in the geometry is fixed everywhere at once.

the chain
lucide geometry
  └─ generator  bow, drift, validate
       └─ @sketchyicons/data          committed JSON, the source of truth
            ├─ @sketchyicons/static   svg files, a sprite, icons.json
            ├─ @sketchyicons/react
            ├─ @sketchyicons/react-native
            ├─ @sketchyicons/vue
            ├─ @sketchyicons/preact
            ├─ @sketchyicons/solid
            ├─ @sketchyicons/svelte
            ├─ @sketchyicons/angular
            └─ sketchyicons           the plain DOM target

@sketchyicons/data

The drawn geometry, one named export per glyph, 1500+ of them, in the same node shape Lucide uses. No framework, no dependency, no build step. Reach for it when you are rendering icons yourself: a canvas, a PDF, a server side image, a framework nobody has written a template for yet.

It is also the only package whose diff is worth reading. An upstream geometry bump shows up here as changed path strings and nowhere else, which is why it is committed rather than built.

data
import { Feather, iconNames } from "@sketchyicons/data";

// Feather -> [["path", { d: "M14.7 18.11A2 2 0 0 1 …" }], …]
// iconNames -> ["a-arrow-down", "a-arrow-up", …]

for (const [, { d }] of Feather) canvas.stroke(new Path2D(d));

@sketchyicons/static

One SVG file per glyph, plus a sprite. This is the one that makes the set usable outside JavaScript entirely: Flutter, SwiftUI, a Figma import, a Rails view, plain HTML.

  • icons/feather.svg for a single file, dropped in wherever you keep assets.
  • sprite.svg when you want one request and <use href="#feather" /> at the call site.

Both are generated from data, so a glyph is byte for byte the same whether you import the component, read the JSON or drop in the SVG.

Framework packages

Eight targets, all generated, none written by hand. If changing one prop meant editing 1500+ files, the architecture would be wrong. Every weight below is one icon, minified and brotlied, with the framework excluded.

  • @sketchyicons/react492 Bone component per icon

    React. Peers: react@^18 || ^19. No runtime dependencies.

  • @sketchyicons/react-native744 Bone component per icon

    React Native. Peers: react@^18 || ^19, react-native-svg@>=13. No runtime dependencies.

  • @sketchyicons/vue466 Bone component per icon

    Vue. Peers: vue@^3. No runtime dependencies.

  • @sketchyicons/preact467 Bone component per icon

    Preact. Peers: preact@^10. No runtime dependencies.

  • @sketchyicons/solid740 Bone component per icon

    Solid. Peers: solid-js@^1.8. No runtime dependencies.

  • @sketchyicons/svelte268 Bone component, icons as data

    Svelte. Peers: svelte@^5. No runtime dependencies.

  • @sketchyicons/angular1.18 kBone component, icons as data

    Angular. Peers: @angular/core@^21, @angular/common@^21. Runtime: tslib@^2.3.

  • sketchyicons450 Bone component per icon

    Plain DOM. No peer dependencies. No runtime dependencies.

Why two shapes

React, React Native, Vue, Preact and Solid get one component per icon, because a component costs nothing in those frameworks and one file per icon is what lets a bundler drop the rest. Svelte and Angular compile a component in every consumer's build, so 1500+ of them would be 1500+ compilations in a project that imports three. Those two ship one component that takes the icon as data.

What a version number means

  • An upstream geometry bump is at least a minor, never a patch. Nobody taking a patch upgrade expects their icons to change shape.
  • All packages are versioned together and released from one repository, so the numbers line up across targets.
  • Tags carry the package name, as in @sketchyicons/react@1.2.0. A bare v1.2.0 would be ambiguous across the set.
Install one