About goey-toast
goey-toast is a skill for adding animated notifications to React apps. Built on Sonner and Framer Motion, it morphs toasts between pill and blob shapes and includes notification states, queue controls, and interaction options.
Key features
- 01Pill-to-blob morphing animation
- 02Success, error, warning, and info toasts
- 03Promise loading and result notifications
- 04Update, filter, or dismiss toasts
- 05Progress, timestamps, and queue options
Use cases
- 01Add notification UI to a React app
- 02Show loading and results for async saves
- 03Offer clickable actions in notifications
- 04Manage toast queues and dismissal
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ anl331/ goey-toast --skill goey-toastAfter installing, type/goey-toastin Claude Code to confirm it loaded.
Details
From the project READMEgoey-toast
A gooey, morphing React toast component built on Sonner with Framer Motion animations (pill → blob → pill). Use this skill to install the package and write correct usage code in any React project.
When to use
- Adding toast / notification UI to a React app (React >= 18).
- The user mentions "goey-toast", "gooey toast", or wants animated/morphing toasts.
- Replacing raw
sonnertoasts with the gooey variant.
Install
npm install goey-toast react react-dom framer-motion
Peer deps (must exist in the host app):
| Package | Version |
|---|---|
| react | >= 18.0.0 |
| react-dom | >= 18.0.0 |
| framer-motion | >= 10.0.0 |
shadcn/ui projects
npx shadcn@latest add https://goey-toast.vercel.app/r/goey-toaster.json
Installs a wrapper at components/ui/goey-toaster.tsx and auto-installs deps.
Two required steps to render toasts
- Mount
<GooeyToaster />once near the app root. - Import the stylesheet once at the entry point. Without it toasts render unstyled.
import { GooeyToaster, gooeyToast } from 'goey-toast'
import 'goey-toast/styles.css' // REQUIRED — import once at app entry
function App() {
return (
<>
<GooeyToaster position="bottom-right" />
<button onClick={() => gooeyToast.success('Saved!')}>Save</button>
</>
)
}
API: gooeyToast
gooeyToast(title, options?) // default (neutral)
gooeyToast.success(title, options?) // green
gooeyToast.error(title, options?) // red
gooeyToast.warning(title, options?) // yellow
gooeyToast.info(title, options?) // blue
gooeyToast.promise(promise, data) // loading → success/error
gooeyToast.update(id, options) // update an existing toast in-place
gooeyToast.dismiss(idOrFilter?) // dismiss one, by type, or all
Common options (2nd arg)
| Option | Type | Notes |
|---|---|---|
description | ReactNode | String or component body |
action | GooeyToastAction | { label, onClick, successLabel? } |
icon | ReactNode | Custom icon (null clears in update) |
duration | number | ms |
id | string | number | Stable id (needed for update) |
fillColor / borderColor / borderWidth | colors / px | Blob styling |
spring / bounce | boolean / number | bounce 0.05–0.8, default 0.4 |
preset | 'smooth' | 'bouncy' | 'subtle' | 'snappy' | Animation preset |
showProgress | boolean | Countdown bar |
showTimestamp | boolean | Default true |
onDismiss / onAutoClose | (id) => void | Dismiss callbacks |
<GooeyToaster /> key props
position ('top-left'|'top-center'|'top-right'|'bottom-left'|'bottom-center'|'bottom-right', default 'bottom-right'),
theme ('light'|'dark'), closeButton, showProgress, closeOnEscape,
maxQueue, queueOverflow ('drop-oldest'|'drop-newest'), dir ('ltr'|'rtl'),
spring, bounce, preset, swipeToDismiss, showTimestamp, gap, offset.
Recipes
Description + action button
gooeyToast.info('Share link ready', {
description: 'Your link has been generated.',
action: {
label: 'Copy',
onClick: () => navigator.clipboard.writeText(url),
successLabel: 'Copied!', // morphs back to pill after click
},
})
Promise toast
gooeyToast.promise(saveData(), {
loading: 'Saving...',
success: (data) => `Saved ${data.count} items`,
error: (e) => `Failed: ${String(e)}`,
description: { success: 'All changes synced.', error: 'Try again later.' },
action: { error: { label: 'Retry', onClick: () => retry() } },
})
Update in place
const id = gooeyToast('Uploading...', { icon: <Spinner /> })
gooeyToast.update(id, { title: 'Done', type: 'success', icon: null })
Dismiss by filter
gooeyToast.dismiss(id) // one
gooeyToast.dismiss({ type: 'error' }) // all errors
gooeyToast.dismiss({ type: ['error','warning'] })
gooeyToast.dismiss() // all
Gotchas
- Forgetting
import 'goey-toast/styles.css'→ unstyled toasts. Most common bug. <GooeyToaster />must be mounted exactly once, near the root.update()requires the sameidreturned from the create call.framer-motionis a peer dep — install it explicitly.- Backward-compat aliases exist (
GoeyToaster,goeyToast) for v0.2.x; prefer the double-onames (GooeyToaster,gooeyToast).
Reference
- Live demo & docs: https://goey-toast.vercel.app
- npm: https://www.npmjs.com/package/goey-toast