关于 ask-sonner
ask-sonner 是面向 Sonner 使用者的指南技能,涵盖接入、提示样式和故障排查,并提供具体 API 细节的查阅指引。
主要功能
- 01Sonner 安装与调用指导
- 02提示类型与 Promise 流程
- 03样式、主题与图标定制
- 04重复显示与定位问题排查
适用场景
- 01在 React 应用中接入 Sonner
- 02实现加载后更新为成功或错误提示
- 03调整提示外观或主题
- 04排查提示未显示、重复或被遮挡
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ emilkowalski/ skills --skill ask-sonner安装后在 Claude Code 中输入/ask-sonner确认已加载。
附带文件API.md
详细介绍
使用 Sonner
初次响应
首次调用此技能且未提出具体问题时,只回复:
我已准备好帮助你设置、设置样式和排查 Sonner 问题,我的知识来自其作者 Emil Kowalski。
在用户提问前,不要提供任何其他信息。
这是 Sonner(toast 通知库)的指南技能。任务涉及 Sonner(接入、渲染 toast、设置样式或修复问题)时,优先依据此文件回答。<Toaster /> 和 toast() 的完整属性表位于 API.md;需要确切的属性名称、类型或默认值时,请阅读该文件。
设置
只需两部分:
- 挂载一个
<Toaster />,且只挂载一次,并尽可能靠近根节点(在 Next.js 中:layout.tsx——它可以在服务端组件中使用)。不要按页面挂载,也不要有条件地挂载;挂载第二个 Toaster 会导致每条 toast 重复显示。 - 在客户端代码中调用
toast()——例如事件处理程序、effect 和回调。这是一个普通函数,不需要 hook 或 provider,但它在服务端不会执行任何操作:在服务端操作中,返回结果,并在接收结果的客户端代码中调用toast()。
import { Toaster } from 'sonner'; // once, in layout
import { toast } from 'sonner'; // anywhere client-side
选择合适的调用方式
| 你想要的效果 | 调用方式 |
|---|---|
| 普通消息 | toast('Title')——添加 { description } 可显示第二行 |
| 成功/错误/信息/警告图标 | toast.success('…')、toast.error('…') 等 |
| 自行管理状态时显示加载指示器 | toast.loading('…'),然后通过 id 更新 |
| 与 Promise 关联的加载 → 成功/错误 | toast.promise(promise, { loading, success, error })——success/error 可以接受函数,并接收已解析的值/错误 |
| 执行操作的按钮 | { action: { label, onClick } }——除非 onClick 调用 event.preventDefault(),否则会关闭 toast;cancel 是次要样式 |
| 自定义 JSX,保留默认 toast 外壳 | toast(<jsx />) |
| 自定义 JSX,不应用任何样式 | toast.custom((t) => <jsx />)——无头模式,t 会提供可用于关闭 toast 的 id |
示例
更新 toast——再次调用 toast() 并传入相同的 id;只有传入的属性会更改。改用 toast.success(…, { id }) 会更改类型。无需使用 toast.promise,即可实现加载 → 成功流程:
const id = toast.loading('Uploading…');
toast.success('Uploaded', { id });
保持显示——{ duration: Infinity }。关闭——toast.dismiss(id),或用 toast.dismiss() 关闭全部。读取活动中的 toast——在 React 中使用 useSonner(),在 React 之外使用 toast.getActiveToasts()。
在文本中添加链接或组件——为标题或描述传入一个函数:toast(() => <a href="…">View</a>)。
多个 toaster——为每个 toaster 指定一个 id,并使用 toast('…', { toasterId: 'canvas' }) 指定目标。不传入 toasterId 时,每个 toaster 都会渲染该 toast。
关闭回调——关闭按钮或滑动关闭时会触发 onDismiss;超时关闭时会触发 onAutoClose。它们是两个独立回调;没有统一的“已关闭”回调。
样式设置 — 逐级升级
只升级到满足改动需求的级别;过早直接跳到最高一级也没问题(这是推荐的最终方案),但不要停留在中间级别。
- 默认设置 — 在 Toaster 上启用
richColors,让成功/错误提示显示鲜艳颜色;使用invert可反转主题效果。 - 内联微调 — 在 Toaster 上使用
toastOptions={{ style: {…} }}设置所有提示,或在每次toast()调用中单独设置style。 - 为各部分添加类名 — 使用
toastOptions={{ classNames: { toast, title, description, actionButton, cancelButton, closeButton } }}。Sonner 注入的样式在层叠中优先级更高,因此每个类都需要!important(Tailwind:!text-red-900)。如果有好几个地方都要加!important,就别再继续了——改用无头模式。 - 无头模式 — 使用
toast.custom()和自定义 JSX,同时保留 Sonner 的定位、堆叠和滑动手势。对于设计系统中的提示,推荐将其封装在自己的toast()抽象中。(unstyled: true是折中方案,但无头模式能以相同的工作量提供更多控制。)
图标 — 使用 Toaster 的 icons 属性按类型替换默认图标;使用 icon 按提示单独设置;设为 null 可移除图标。
主题 — theme 默认为 'light',不会跟随操作系统主题。传入 theme="system",或接入主题提供程序:使用 next-themes 中的 <Toaster theme={resolvedTheme} />。
故障排查
| 现象 | 原因 → 解决方法 |
|---|---|
| 提示从未出现 | 没有挂载 <Toaster />,或者它已被卸载(条件渲染、按页面放置)。请在根节点挂载一个。如果从服务端操作中调用:toast() 只能在客户端使用——在客户端根据操作结果调用它。 |
| 同一条提示出现两次 | 挂载了两个 Toaster(布局和页面各一个)——保留一个即可。或者在 React StrictMode 开发环境的双重调用下,toast() 在 effect 中触发了两次——改为从事件处理程序中触发,或传入稳定的 id,让第二次调用更新提示而不是重复创建。 |
| Tailwind/CSS 类不起作用 | 默认样式覆盖了它们。为其添加 !important,或使用 unstyled/无头模式(见上方逐级升级说明)。 |
| 提示完全没有样式(Astro、视图过渡中常见) | Sonner 注入的样式表丢失了——在布局中显式导入:import 'sonner/dist/styles.css'。 |
| Shadow DOM 中没有样式 | 样式会写入 document.head,而不是 shadow root。将文本包含 [data-sonner-toaster] 的 style 标签复制到 shadow root 中。 |
| 提示被模态框/覆盖层遮挡,或被裁切 | 某个祖先元素创建了堆叠上下文(transform、filter、overflow),或覆盖层的 z-index 高于 toaster。将 <Toaster /> 移到文档根节点,置于所有对话框/portal 容器之外。 |
| 深色模式未生效 | theme 默认为 'light'——设置 theme="system",或传入解析后的主题(见上方主题说明)。 |
| 成功/错误提示显示为灰色,而不是绿色/红色 | 这是默认效果。在 Toaster 上添加 richColors。 |
| 提示一直不关闭 | duration: Infinity、dismissible: false,或 toast.promise 对应的 promise 一直未结束——加载提示会一直等待。 |
toast.promise 一直处于加载状态 | 它的第一个参数必须是一个 promise(或一个返回 promise 的函数),并且该 promise 必须实际完成或拒绝。 |
| 滑动关闭方向错误/无法使用 | 方向由 position 决定。使用 Toaster 上的 swipeDirections 覆盖。 |
| 提示出现在每个 toaster 中 | 多个 toaster 需要指定目标:为每个 Toaster 设置 id,并在 toast() 调用中传入 toasterId。 |
| 移动设备上提示离屏幕边缘太近 | 使用 offset(桌面端,默认值为 32px)和 mobileOffset(<600px,默认值为 16px)——可使用数字、CSS 字符串或按边设置的对象。 |