Visual Effects
Confetti
A canvas burst of paper pieces, fired on demand.
Confetti draws its own paper pieces on a canvas, so a burst costs nothing until you ask for one. Mount the component once, then call fire() from a ref when something deserves celebrating.
Each piece gets its own launch angle, speed, colour and spin, then falls under gravity while it fades out. The animation loop shuts down as soon as the last piece lands, so an idle overlay burns no frames.
The overlay is fixed to the viewport and ignores pointer events by default. Override the root slot to park it inside a card instead.
Usage
Fire a burst from the middle of the card
<script setup lang="ts">
import { ref } from 'vue'
const confetti = ref<{ fire: (options?: Record<string, unknown>) => void } | null>(null)
</script>
<template>
<div class="relative flex h-64 w-full flex-col items-center justify-center gap-4 overflow-hidden rounded-xl border border-default bg-default">
<MConfetti ref="confetti" :ui="{ root: 'absolute inset-0 z-10' }" />
<button
class="rounded-lg bg-primary px-4 py-2 text-sm font-medium text-inverted"
@click="confetti?.fire({ particleCount: 120, spread: 70 })"
>
Celebrate
</button>
</div>
</template>
Examples
Customize
<script setup lang="ts">
import { ref } from 'vue'
const confetti = ref<{ fire: (options?: Record<string, unknown>) => void } | null>(null)
const presets = [
{ label: 'Wide spray', options: { particleCount: 140, spread: 80 } },
{ label: 'Tight fountain', options: { particleCount: 60, spread: 20, duration: 3 } },
{ label: 'Left corner', options: { particleCount: 70, spread: 55, x: 40, y: 220 } },
]
</script>
<template>
<div class="relative flex h-64 w-full flex-wrap items-center justify-center gap-3 overflow-hidden rounded-xl border border-default bg-default">
<MConfetti ref="confetti" :ui="{ root: 'absolute inset-0 z-10' }" />
<button
v-for="preset in presets"
:key="preset.label"
class="rounded-lg border border-default bg-elevated px-4 py-2 text-sm font-medium text-highlighted"
@click="confetti?.fire(preset.options)"
>
{{ preset.label }}
</button>
</div>
</template>
Methods
| Prop | Type | Default | Description |
|---|---|---|---|
fire | (options?: ConfettiFireOptions) => void | - | Launches one burst. Safe to call while a previous burst is still falling, and a no-op when the visitor has asked for reduced motion. |
fire() options
| Prop | Type | Default | Description |
|---|---|---|---|
particleCount | number | - | Pieces to launch. Defaults to the particleCount prop. |
spread | number | - | Half-angle of the launch cone, in degrees. 90 sprays a full circle. Defaults to the spread prop. |
duration | number | - | Seconds the burst lasts. Defaults to the duration prop. |
colors | string[] | - | Colours to draw from. Defaults to the colors prop. |
x | number | - | Horizontal launch point, in pixels from the canvas edge. Defaults to the centre. |
y | number | - | Vertical launch point, in pixels from the canvas edge. Defaults to the centre. |
Props
| Prop | Type | Default | Description |
|---|---|---|---|
particleCount | number | 80 | Pieces launched per burst. |
spread | number | 60 | Half-angle of the launch cone, in degrees. 90 sprays a full circle. |
duration | number | 2.4 | Seconds a burst lasts. Each piece fades out somewhere between three quarters of this and the full value. |
colors | string[] | ['#22d3ee', '#a855f7', '#f97316', '#22c55e', '#facc15'] | Colours a burst picks from, cycled in order. |
class | string | '' | CSS classes to apply to the overlay. |
UI
This component's theme exposes the slots below. Override any of them per instance with the ui prop, or app-wide under ui.confetti in app.config.ts.
| Prop | Type | Default | Description |
|---|---|---|---|
ui | object | - | Per-slot classes merged over the theme defaults. |
| Slot | Props | Description |
|---|---|---|
root | - | pointer-events-none fixed inset-0 z-50 |
canvas | - | size-full |