Jot

Hand-Drawn Annotation Arrow for React

A handwritten note that draws its own arrow to the thing it's about. It writes itself in when it scrolls into view, keeps the arrow attached as the layout moves, swings it over when you point it somewhere new, and rubs itself out once it has done its job.

  • Annotation
  • SVG
  • Onboarding

Uses motion, clsx, tailwind-merge

Inspired by ToDesktop

Open in ChatGPTOpen in Claude
from
at
bend0.35
loopfalse
gap6px
tilt0°
delay0.00s(replays the demo)
speed1.00×(replays the demo)
<Jot />

replays the demo when you let go.

Wren Forms

Forms people actually finish.

One question at a time, saved as they type, easy to answer on a phone.

Replies todayLive

1,284

Finish rate71%

free for your first 1,000 replies
real replies,
counted as they land
Take this demo to your coding agent.
Add this moment from KeyframeUI to our project and adapt it to our content.

Jot: A handwritten note that draws its own arrow to the thing it's about. It writes itself in when it scrolls into view, keeps the arrow attached as the layout moves, swings it over when you point it somewhere new, and rubs itself out once it has done its job.
This demo, Annotated Hero: A form builder's hero. Two notes write themselves in, one curling its arrow up to the call to action, the other pointing at the live reply count.

1. Install it:
   npx shadcn@latest add @keyframeui/jot-demo
   (If the CLI can't find @keyframeui, use https://keyframeui.com/r/jot-demo.json instead.)
   It adds the demo, components/jot-demo.tsx, and the component, components/ui/jot.tsx.

2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <Jot> on its own.

3. These props set its feel. Change them before you change the file:
   - from (default "auto"): The side of the note the arrow leaves from. `"auto"` picks the side facing the target.
   - at (default "auto"): The side of the target the arrow lands on. `"auto"` aims at its center and stops at its edge.
   - bend (default 0.35): How far the arrow bows, from -1 to 1. 0 is nearly straight; flip the sign to bow the other way.
   - loop (default false): Curl the arrow once midway, like a quick flourish.
   - gap (default 6): Pixels left between the arrowhead and the target.
   - tilt (default 0): Degrees to turn the words, for a looser hand. The arrow isn't turned.
   - delay (default 0): Seconds to wait before the first write, to write several notes one after another.
   - speed (default 1): How fast the pen writes, draws the arrow and rubs out, as a multiple of the default pace. `2` is twice as fast.

4. Don't change, in components/ui/jot.tsx:
   - The pen lives outside React: it reads both boxes in motion's `frame.read` and paints in `frame.render`, only while something could move a box, and never off screen.
   - The words are real text shown by a `clip-path` wipe, line by line; the strokes draw by `strokeDasharray` on `pathLength={1}` paths, and an undrawn one is hidden outright, since a zero-length dash still paints a round cap.
   - The pen writes the words, sets off on the shaft before the last word is done, then draws the head's two strokes; rubbing out runs backward: head, shaft, words.
   - The tremor is seeded from the words and fades out at both ends, so the line never shimmers and still starts and lands exactly.
   - While shown, the target lists the note in `aria-describedby`; reduced motion drops the pen and fades the note and arrow in and out by opacity.
   Its timing is all in the TIMING block at the top of that file.

Docs: https://keyframeui.com/components/jot.md

Installation

Add the component on its own with the CLI, or copy the file in by hand.

The CLI writes components/ui/jot.tsx and installs motion, clsx and tailwind-merge. It needs the @keyframeui registry in components.json; add it once per project.

npx shadcn@latest add @keyframeui/jot

Examples

Finished sections built with Jot. Each one installs whole, with the component included.

Follow the Pick

Three plans as radio cards. The note swings its arrow to whichever plan you pick and rewrites itself for it.

Lantern plans

Client portals for small studios

14 days free, then $24 a month

Start with Studio
most studios start here
Take this demo to your coding agent.
Add this moment from KeyframeUI to our project and adapt it to our content.

Jot: A handwritten note that draws its own arrow to the thing it's about. It writes itself in when it scrolls into view, keeps the arrow attached as the layout moves, swings it over when you point it somewhere new, and rubs itself out once it has done its job.
This demo, Follow the Pick: Three plans as radio cards. The note swings its arrow to whichever plan you pick and rewrites itself for it.

1. Install it:
   npx shadcn@latest add @keyframeui/jot-plans-demo
   (If the CLI can't find @keyframeui, use https://keyframeui.com/r/jot-plans-demo.json instead.)
   It adds the demo, components/jot-plans-demo.tsx, and the component, components/ui/jot.tsx.

2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <Jot> on its own.

3. These props set its feel. Change them before you change the file:
   - from (default "auto"): The side of the note the arrow leaves from. `"auto"` picks the side facing the target.
   - at (default "auto"): The side of the target the arrow lands on. `"auto"` aims at its center and stops at its edge.
   - bend (default 0.35): How far the arrow bows, from -1 to 1. 0 is nearly straight; flip the sign to bow the other way.
   - loop (default false): Curl the arrow once midway, like a quick flourish.
   - gap (default 6): Pixels left between the arrowhead and the target.
   - tilt (default 0): Degrees to turn the words, for a looser hand. The arrow isn't turned.
   - delay (default 0): Seconds to wait before the first write, to write several notes one after another.
   - speed (default 1): How fast the pen writes, draws the arrow and rubs out, as a multiple of the default pace. `2` is twice as fast.

4. Don't change, in components/ui/jot.tsx:
   - The pen lives outside React: it reads both boxes in motion's `frame.read` and paints in `frame.render`, only while something could move a box, and never off screen.
   - The words are real text shown by a `clip-path` wipe, line by line; the strokes draw by `strokeDasharray` on `pathLength={1}` paths, and an undrawn one is hidden outright, since a zero-length dash still paints a round cap.
   - The pen writes the words, sets off on the shaft before the last word is done, then draws the head's two strokes; rubbing out runs backward: head, shaft, words.
   - The tremor is seeded from the words and fades out at both ends, so the line never shimmers and still starts and lands exactly.
   - While shown, the target lists the note in `aria-describedby`; reduced motion drops the pen and fades the note and arrow in and out by opacity.
   Its timing is all in the TIMING block at the top of that file.

Docs: https://keyframeui.com/components/jot.md

Hints That Retire

A file-sharing card where each hint rubs itself out once you've done what it asks, and the next one writes in.

ParcelLinks last 7 days

Send files

No files yet
Your link shows up here
start here: drop
anything in
Take this demo to your coding agent.
Add this moment from KeyframeUI to our project and adapt it to our content.

Jot: A handwritten note that draws its own arrow to the thing it's about. It writes itself in when it scrolls into view, keeps the arrow attached as the layout moves, swings it over when you point it somewhere new, and rubs itself out once it has done its job.
This demo, Hints That Retire: A file-sharing card where each hint rubs itself out once you've done what it asks, and the next one writes in.

1. Install it:
   npx shadcn@latest add @keyframeui/jot-onboarding-demo
   (If the CLI can't find @keyframeui, use https://keyframeui.com/r/jot-onboarding-demo.json instead.)
   It adds the demo, components/jot-onboarding-demo.tsx, and the component, components/ui/jot.tsx.

2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <Jot> on its own.

3. These props set its feel. Change them before you change the file:
   - from (default "auto"): The side of the note the arrow leaves from. `"auto"` picks the side facing the target.
   - at (default "auto"): The side of the target the arrow lands on. `"auto"` aims at its center and stops at its edge.
   - bend (default 0.35): How far the arrow bows, from -1 to 1. 0 is nearly straight; flip the sign to bow the other way.
   - loop (default false): Curl the arrow once midway, like a quick flourish.
   - gap (default 6): Pixels left between the arrowhead and the target.
   - tilt (default 0): Degrees to turn the words, for a looser hand. The arrow isn't turned.
   - delay (default 0): Seconds to wait before the first write, to write several notes one after another.
   - speed (default 1): How fast the pen writes, draws the arrow and rubs out, as a multiple of the default pace. `2` is twice as fast.

4. Don't change, in components/ui/jot.tsx:
   - The pen lives outside React: it reads both boxes in motion's `frame.read` and paints in `frame.render`, only while something could move a box, and never off screen.
   - The words are real text shown by a `clip-path` wipe, line by line; the strokes draw by `strokeDasharray` on `pathLength={1}` paths, and an undrawn one is hidden outright, since a zero-length dash still paints a round cap.
   - The pen writes the words, sets off on the shaft before the last word is done, then draws the head's two strokes; rubbing out runs backward: head, shaft, words.
   - The tremor is seeded from the words and fades out at both ends, so the line never shimmers and still starts and lands exactly.
   - While shown, the target lists the note in `aria-describedby`; reduced motion drops the pen and fades the note and arrow in and out by opacity.
   Its timing is all in the TIMING block at the top of that file.

Docs: https://keyframeui.com/components/jot.md

Props

children Required

TypeReact.ReactNode

The words. Keep them short: a few words on one or two lines.

to

TypeReact.RefObject<Element | null> | string

What the arrow points at: a ref, or a CSS selector looked up inside the nearest [data-jot-scope] ancestor (the whole document without one). Leave it out for a note with no arrow.

from

Type"auto" | "top" | "right" | "bottom" | "left"Default"auto"

The side of the note the arrow leaves from. "auto" picks the side facing the target.

at

Type"auto" | "top" | "right" | "bottom" | "left"Default"auto"

The side of the target the arrow lands on. "auto" aims at its center and stops at its edge.

bend

TypenumberDefault0.35

How far the arrow bows, from -1 to 1. 0 is nearly straight; flip the sign to bow the other way.

loop

TypebooleanDefaultfalse

Curl the arrow once midway, like a quick flourish.

gap

TypenumberDefault6

Pixels left between the arrowhead and the target.

tilt

TypenumberDefault0

Degrees to turn the words, for a looser hand. The arrow isn't turned.

trigger

Type"view" | "mount"Default"view"

When it writes itself: once half of it is on screen, or as soon as it mounts.

delay

TypenumberDefault0

Seconds to wait before the first write, to write several notes one after another.

speed

TypenumberDefault1

How fast the pen writes, draws the arrow and rubs out, as a multiple of the default pace. 2 is twice as fast.

show

TypebooleanDefaulttrue

Set to false to rub the note out; true writes it back in.

describe

TypebooleanDefaulttrue

Describe the target with the note's words for screen readers, through aria-describedby.

className

Typestring

Extra classes for the note: where it sits, its text color (the arrow takes it too), size and width. The words use a stack of system handwriting fonts; set your own with a font-* class.