Particle Dissolve

Break any element into drifting dust when it's deleted, sent or dismissed, with its real colours and a smooth fold.

Preview

Setup

Install with the shadcn CLI

$npx shadcn@latest add https://kinetikui.vercel.app/r/particle-dissolve.json

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

    Import and render:

    Example
    import { 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

OptionTypeDefaultNotes
from"left" | "right" | "top" | "bottom" | "center""left"Where the element starts breaking up.
wind[x, y]WIND.risePush on every particle. Presets: WIND.rise, fall, left, right.
turbulencenumber0.012How much particles wobble off the wind.
particleSizenumber2Smallest particle in px. Bigger is chunkier and cheaper.
maxParticlesnumber8000Upper bound per element.
sweepnumber520Ms for the break-up to cross the element.
lifetimenumber1100Ms one particle lives after it lets go.
collapsebooleantrueFold the element's space away so neighbours slide in.

Accessibility

  • The dust canvas is decorative and hidden from screen readers
  • The element is made inert while 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