sketchyicons

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.

Browse the set
feather24 x 24 · 3 paths · v0.1.0
  • d1 M14.38 18.27C13.6 18.29 13.13 19.03 12.69 18.91Q8.84 19.41 4.85 18.61Q6.37 15.16 5.01 11.32C5.42 10.72 5.68 10.6 5.62 10.06Q10.21 8.38 12.33 3.61C13.33 2.16 15.87 1.07 17.74 2.33C19.62 2.22 21.49 3.75 21.84 6.19C21.96 7.94 21.64 11.44 19.94 12.27Q15.94 14.1 14.55 18.41
  • d2 M15.98 7.97Q8.98 14.98 1.81 22.11
  • d3 M17.46 14.92Q13.24 15.58 9.13 14.93
trophy
compass
flame
heart
anvil

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.

  1. Where it starts

    Exact coordinates on a 24 unit grid. Straight runs stay straight, which is what makes a generated set look generated.

  2. 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

4 glyphs rendered at 15, 20, 24, 40 pixels
glyph15 px20 px24 px40 px
leaf
compass
flask-conical
trophy
A specimen at 15 pixels beside 13 pixel text, which is where these glyphs spend their life. Nothing here is clickable.

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
  • mail
  • 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.

Open the full catalogue

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.

  • leaf
  • compass
  • flask-conical
  • trophy

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 pixels. Defaults to 24.
color
string Stroke color. Defaults to currentColor.
strokeWidth
number Defaults to 2. Thinner reads better above 32.
fill
string Read only by the glyphs that have a solid state.

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/datapublished

    The drawn geometry as JSON. No framework, no dependency, and the source every other package is generated from.

  • @sketchyicons/staticpublished

    One SVG file per icon, plus a sprite. This is the one you reach for from Flutter, SwiftUI, Figma or plain HTML.

  • @sketchyicons/reactpublished

    One component per icon, tree shakeable, with no runtime dependency.

  • @sketchyicons/react-nativepublished

    The same components on react-native-svg, which is the only peer dependency.

  • vue, svelte, angular, solidnot yet

    Additional generation templates over the same data. Not published yet.

Zero runtime dependencies
One peer dependency per target: react for the web package, react-native-svg for the native one. Nothing else.
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.

Your product does not have to look like everyone else's.

Most interfaces reach for the same set, and it shows. This one keeps the shapes people already recognise and changes the only thing that carries any warmth, which is the line itself. One import, and every screen you ship reads as drawn by somebody.