# Goey Toast

> Install and use goey-toast — a gooey, morphing React toast component built on Sonner with Framer Motion. Use when adding toast/notification UI to a React app (success/error/warning/info/promise toasts), or when the user mentions goey-toast / gooey toast.

- Skill: `anl331/goey-toast` (Agent Skill)
- Install (CLI): `npx skillmds@latest add anl331/goey-toast`
- Raw SKILL.md: https://api.skillmd.com/api/skills/anl331/goey-toast/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- License: MIT
- Author: anl331 (https://skillmd.com/u/anl331)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/anl331/goey-toast

---


# goey-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 `sonner` toasts with the gooey variant.

## Install

```bash
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

```bash
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

1. **Mount `<GooeyToaster />` once** near the app root.
2. **Import the stylesheet once** at the entry point. Without it toasts render
   unstyled.

```tsx
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`

```ts
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

```tsx
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

```tsx
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

```tsx
const id = gooeyToast('Uploading...', { icon: <Spinner /> })
gooeyToast.update(id, { title: 'Done', type: 'success', icon: null })
```

### Dismiss by filter

```tsx
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 same `id` returned from the create call.
- `framer-motion` is a peer dep — install it explicitly.
- Backward-compat aliases exist (`GoeyToaster`, `goeyToast`) for v0.2.x; prefer
  the double-`o` names (`GooeyToaster`, `gooeyToast`).

## Reference

- Live demo & docs: https://goey-toast.vercel.app
- npm: https://www.npmjs.com/package/goey-toast

