Particle Dissolve
Break any element into drifting dust when it's deleted, sent or dismissed, with its real colours and a smooth fold.
Preview
Can you shorten the hero headline?
Sure: “Build beautiful interfaces, fast.”
Perfect. Now delete the old draft.
Done. The old draft is gone.
Setup
Install with the shadcn CLI
One command adds every file, the npm packages and any Kinetik components it builds on. Needs a components.json; the CLI offers to create one if your project has none.
Or install manually
- 1
Create a
components/ui/particle-dissolve/folder and copy every file from the Code tab into it. Each file does one job, so you can read or change a piece without the rest. - 2
Import and render:
Exampleimport { useDissolve } from "@/components/ui/particle-dissolve" const { ref, dissolve } = useDissolve() <div ref={ref}>…</div> <button onClick={async () => { await dissolve(); removeItem() }}>Delete</button>
No dependencies: it is plain React, Canvas and the Web Animations API.
Usage
Attach ref to the element and call dissolve(). The element is copied into pixels, each
pixel becomes a particle in its own colour, and the dust sweeps away on the wind. The promise
resolves once the dust is gone, which is when you remove the item from your data.
import { useDissolve } from "@/components/ui/particle-dissolve"
function Message({ message, onDelete }) {
const { ref, dissolve } = useDissolve<HTMLDivElement>({ from: "left" })
return (
<div ref={ref}>
<p>{message.text}</p>
<button
onClick={async () => {
await dissolve()
onDelete(message.id)
}}
>
Delete
</button>
</div>
)
}
Outside React, call dissolve(element, options) directly. restore(element) brings a dissolved
element back, for an undo.
Space between items should come from margins, not a flex gap, so the fold animates smoothly.
Add data-dissolve-ignore to anything you don't want in the dust, such as the delete button.
How it stays fast
- One canvas for the whole page, and its loop only runs while dust is moving
- Particles live in typed arrays, not one object each, and finished ones are swapped out in place
- Large elements are sampled more coarsely so they never pass
maxParticles - Colours are grouped so the canvas rarely has to change paint
Options
| Option | Type | Default | Notes |
|---|---|---|---|
from | "left" | "right" | "top" | "bottom" | "center" | "left" | Where the element starts breaking up. |
wind | [x, y] | WIND.rise | Push on every particle. Presets: WIND.rise, fall, left, right. |
turbulence | number | 0.012 | How much particles wobble off the wind. |
particleSize | number | 2 | Smallest particle in px. Bigger is chunkier and cheaper. |
maxParticles | number | 8000 | Upper bound per element. |
sweep | number | 520 | Ms for the break-up to cross the element. |
lifetime | number | 1100 | Ms one particle lives after it lets go. |
collapse | boolean | true | Fold the element's space away so neighbours slide in. |
Accessibility
- The dust canvas is decorative and hidden from screen readers
- The element is made
inertwhile it dissolves, so it can't be focused or clicked twice - With
prefers-reduced-motion, the element simply fades out and folds - Announce the change yourself (for example a "Message deleted" toast), since the effect is visual only