SuperAPI AI 工具集

ask-sonner

前端与建站

查看项目

更新于 10-15 08:33

技能介绍

用途

本技能是 Sonner(React toast 通知库)的使用指南,由该库作者 Emil Kowalski 提供知识来源。当任务涉及 Sonner 的接入、渲染 toast、样式定制或问题修复时,优先依据本指南作答。关于 <Toaster /> 和 toast() 的完整属性表位于 API.md,需要精确的属性名、类型或默认值时查阅该文件。

主要能力

安装与接入

Sonner 的接入只有两部分:

  1. 一个 <Toaster />,只挂载一次,尽量靠近根节点(在 Next.js 中放在 layout.tsx,它可以在服务端组件内工作)。不要按页面渲染或条件渲染;挂载第二个 Toaster 会导致每个 toast 重复显示。
  2. 在客户端代码中调用 toast()——事件处理器、effect、回调。它是普通函数,不需要 hook 或 provider,但在服务端不起作用:在 server action 中应返回结果,并在接收结果的客户端代码里调用 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 提供用于关闭的 id

常用做法

  • 更新 toast:用相同 id 再次调用 toast(),只改变传入的属性。切换为 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:给每个设置 id,并用 toast('…', { toasterId: 'canvas' }) 指定目标。不传 toasterId 时,每个 toaster 都会渲染该 toast。
  • 关闭回调:onDismiss 在点击关闭按钮或滑动时触发;onAutoClose 在超时时触发。两者独立,没有单一的“已关闭”回调。

样式定制——逐级升级

只在改动需要时才升级;过早跳到最高层级也没问题(这是推荐的最终状态),但停留在中间层级不可取。

  • 默认:在 Toaster 上加 richColors 可获得彩色的成功/错误样式,invert 可反转主题。
  • 内联微调:在 Toaster 上用 toastOptions={{ style: {…} }} 作用于所有 toast,或在每次 toast() 调用时设置 style。
  • 为各部分加类名:toastOptions={{ classNames: { toast, title, description, actionButton, cancelButton, closeButton } }}。Sonner 注入的样式在层叠中优先,因此每个类名都需要 !important(Tailwind:!text-red-900)。如果标记了多处 important,就停下来——改用无头模式。
  • 无头:用 toast.custom() 搭配自己的 JSX,保留 Sonner 的定位、堆叠和滑动。设计系统 toast 的推荐做法是包装成自己的 toast() 抽象。(unstyled: true 是折中方案,但无头模式在同等投入下控制力更强。)
  • 图标:用 Toaster 的 icons 属性按类型替换默认图标,用 icon 按 toast 替换,用 null 移除。
  • 主题:theme 默认为 'light',不跟随系统。传入 theme="system",或接入主题 provider:来自 next-themes 的 <Toaster theme={resolvedTheme} />。

故障排查

  • toast 从不出现:没有挂载 <Toaster />,或它被卸载(条件渲染、按页面放置)。在根节点挂载一个。如果从 server action 调用:toast() 仅限客户端——在客户端用 action 的结果调用它。
  • 同一 toast 出现两次:挂载了两个 Toaster(layout 和 page)——保留一个。或在 React StrictMode 开发环境双重调用下在 effect 中触发 toast()——改为从事件处理器触发,或传入稳定 id 使第二次调用更新而非重复。
  • Tailwind/CSS 类名无效:默认样式覆盖了它们。标记为 !important,或使用 unstyled / 无头(见上文层级)。
  • toast 完全无样式渲染(常见于 Astro、视图过渡):Sonner 注入的样式表丢失——在 layout 中显式导入:import 'sonner/dist/styles.css'。
  • Shadow DOM 内无样式:样式落在 document.head,而非 shadow root。将文本包含 [data-sonner-toaster] 的 style 标签复制到 shadow root 中。
  • toast 被模态框/浮层遮挡或被裁剪:某个祖先创建了层叠上下文(transform、filter、overflow),或浮层的 z-index 高于 toaster。将 <Toaster /> 移到文档根节点,放在任何 dialog/portal 容器之外。
  • 深色模式被忽略:theme 默认为 'light'——设置 theme="system" 或传入解析后的主题(见上文主题)。
  • 成功/错误显示为灰色而非绿/红:这是默认行为。在 Toaster 上加 richColors。
  • toast 从不关闭:duration: Infinity、dismissible: false,或 toast.promise 的 promise 永不结束——加载 toast 会一直等待。
  • toast.promise 卡在加载中:它需要 promise(或返回 promise 的函数)作为第一个参数,且该 promise 必须真正 resolve/reject。
  • 滑动关闭方向错误或不工作:方向由 position 推导。用 Toaster 上的 swipeDirections 覆盖。
  • toast 出现在每个 toaster 中:多个 toaster 需要指定目标:给每个 Toaster 一个 id,并在 toast() 调用中传入 toasterId。
  • 移动端 toast 离屏幕边缘太近:offset(桌面端,默认 32px)和 mobileOffset(<600px,默认 16px)——可以是数字、CSS 字符串或按边的对象。

适用场景

适用于在 React 项目(含 Next.js)中接入 Sonner、渲染各类 toast、定制样式、处理多 toaster 与主题,以及排查 toast 不显示、重复、样式失效、遮挡、不关闭等问题。

限制

本指南为 Sonner 使用说明,不涉及其他 toast 库。toast() 仅限客户端调用,服务端需通过返回结果在客户端触发。完整属性表需查阅 API.md。