1500+ icons that look
hand drawn.
Nobody drew them. A generator takes Lucide's geometry and bends every line, seeded per icon so your build and mine produce it byte for byte. Same names, same props, so switching over is one import line.
- d1 M14.38 18.27C13.63 18.36 13.16 19.09 12.69 18.91Q8.84 18.99 4.85 18.61Q5.96 15.16 5.01 11.32C5.27 10.65 5.53 10.54 5.62 10.06Q9.67 7.83 12.33 3.61C13.37 2.33 15.91 1.25 17.74 2.33C19.4 2.43 21.27 3.96 21.84 6.19C21.94 7.93 21.62 11.44 19.94 12.27Q16.33 14.5 14.55 18.41
- d2 M15.98 7.97Q8.77 14.77 1.81 22.11
- d3 M17.46 14.92Q13.24 15.56 9.13 14.93
How the hand gets added
Two passes over the path data. Nothing is redrawn; the coordinates move. Below is feather, upstream on the left and published on the right, at the same size and the same stroke.
Where it starts
Exact coordinates on a 24 unit grid. Straight runs stay straight, which is what makes a generated set look generated.
Where it lands
Every straight run becomes a quadratic whose control point sits off the midpoint by a fraction of the run's own length. Then every coordinate moves. Both are seeded from the icon name, so the result is identical on every machine.
Chrome is quiet, content has a hand
A coordinate cannot wander further than the run it belongs to and still be that run, so tight shapes barely move. That is the point rather than a limitation: everybody knows exactly what a chevron looks like, so a shaky one reads as broken, while a feather can wander and still look deliberate.
Both of these are shipped geometry with the upstream original underneath. Nothing was tuned for this page.
- chevron-right
- barely moves
- feather
- wanders
It has to hold at 15 pixels
That is the size an interface actually uses inside a control, and it is where a wobbly stroke turns to mud. An icon that only reads at 24 is not finished.
rendered at 15, 20, 24, 40 px
| glyph | 15 px | 20 px | 24 px | 40 px |
|---|---|---|---|---|
| leaf | ||||
| compass | ||||
| flask-conical | ||||
| trophy |
1500+ icons, drawn
Search by name. Point at one and the plotter goes over it again.
93 shown, type to search all of them
- feather
- compass
- flame
- anvil
- leaf
- bird
- heart
- star
- trophy
- crown
- sparkles
- flask-conical
- book-open
- bookmark
- quote
- pen-line
- pencil-ruler
- ruler
- camera
- music
- coffee
- umbrella
- ticket
- fish
- globe
- map-pin
- rocket
- anchor
- moon
- sun
- cloud
- zap
- lightbulb
- magnet
- paperclip
- scissors
- package
- folder
- git-branch
- terminal
- key
- lock
- send
- message-circle
- bell
- calendar
- user
- telescope
- hammer
- shovel
- axe
- wrench
- drill
- tent
- mountain
- waves-horizontal
- snowflake
- sprout
- trees
- guitar
- drum
- piano
- mic-vocal
- radio
- headphones
- croissant
- ice-cream-cone
- pizza
- cake-slice
- wine
- beer
- shirt
- glasses
- watch
- footprints
- gem
- cat
- dog
- rabbit
- squirrel
- snail
- shell
- origami
- swords
- dices
- puzzle
- palette
- brush
- microscope
- atom
- dna
- orbit
Each glyph is its own file. Importing one icon pulls in one icon, and size-limit proves that in CI rather than assuming it.
The original underneath, in blue
The source geometry is the blue line, the drawn one sits on top of it. Every departure you can see is a coordinate that moved, and none of it is a redraw. How far it moves is the hand setting, noted under each one.
The props you already pass
If Lucide is already in the project, the change is the import line. Nothing else about the call site moves.
import { Feather, Compass } from "@sketchyicons/react";
// same call site as before
<Feather size={20} strokeWidth={1.75} />- size
- number Width and height in one prop. Defaults to 24.
- color
- string The stroke. Defaults to currentColor.
- strokeWidth
- number Defaults to 2. Thinner reads better above 32.
- absoluteStrokeWidth
- boolean Keeps the stroke the same visual width whatever the size. Off by default.
- fill
- string Fills the shape. Defaults to none.
One package per framework
Each target carries its own peer dependencies, so a Vue project never sees React in its tree. All of them are generated from the same geometry, so a glyph is identical whichever package you install it from.
- @sketchyicons/react492 B
One component per icon, tree shakeable, with react as the only peer dependency.
- @sketchyicons/react-native744 B
The same components on react-native-svg, which is the second peer dependency.
- @sketchyicons/vue466 B
One component per icon for Vue 3, with vue as the only peer dependency.
- @sketchyicons/preact467 B
One component per icon, with attributes in SVG's own spelling because Preact does not rewrite them.
- @sketchyicons/solid740 B
One component per icon, props read reactively, built without a JSX compile step of its own.
- @sketchyicons/svelte268 B
One component, icons as data. A Svelte component is a file your bundler compiles, so 1756 of them would be 1756 compilations.
- @sketchyicons/angular1.18 kB
One component, icons as data, for the same reason. tslib is its only runtime dependency, as it is for any Angular library.
- sketchyicons450 B
The plain DOM target. createIcons replaces the elements you mark up, createElement builds one svg.
- @sketchyicons/data250 B
The drawn geometry as named constants. No framework, no dependency, and the source every other package is generated from.
- @sketchyicons/static
One SVG file per icon, plus a sprite and the geometry as JSON. This is the one to reach for from Flutter, SwiftUI, Figma or plain HTML.
- Almost no dependencies
- Every package declares its framework as a peer and stops there. Only the Angular library carries a runtime dependency, tslib, which every Angular library does.
- Byte identical rebuilds
- Two generator runs produce the same files. A test asserts it, so an upstream bump shows up as a reviewable diff instead of noise.
- One file per icon
- A barrel of 1500+ exports would undo your bundler's work. Tree shaking is measured in CI, not assumed.
- Lucide's own props
- size, color, strokeWidth and fill, forwarded to the SVG the same way. Migrating is one import line.