Sonner
Уведомления в виде наложенных друг на друга тостов для подтверждения сохранения, загрузки и окон отмены действий — с использованием необруталистского стиля оформления с рамками и тенями.
"use client"
import { toast } from "sonner"Sonner выводит стопку auto-dismiss toast'ов одним вызовом toast() — смонтируйте один <Toaster /> и шлите уведомления откуда угодно, без wiring context. Это не примитив Radix или Base UI: обе backend-вариации оборачивают одну и ту же библиотеку Sonner от emilkowalski, оформлен по рецепту необрутализма: толстые рамки, жёсткие тени и жирный шрифт.
Когда это пригодится:
- Feedback действий — «Сохранено», «Скопировано в буфер», «Настройки обновлены» после mutation.
- Асинхронные операции —
toast.promise()ведёт upload или deploy от loading до success или error в одном toast. - Окна undo — удаления и архивы с action-кнопкой, чтобы у destructive-операций был grace period.
О компоненте#
Sonner создан и поддерживается emilkowalski.
Установка#
Выполните следующую команду:
pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/sonner.jsonnpx shadcn@latest add https://neobrutalism.com/r/base/sonner.jsonyarn dlx shadcn@latest add https://neobrutalism.com/r/base/sonner.jsonbunx --bun shadcn@latest add https://neobrutalism.com/r/base/sonner.json
Добавьте компонент Toaster.
import { Toaster } from "@/components/ui/sonner"
export default function RootLayout({ children }) {
return (
<html lang="en">
<head />
<body>
<main>{children}</main>
<Toaster />
</body>
</html>
)
}Установите следующие зависимости:
pnpm add sonner next-themesnpm install sonner next-themesyarn add sonner next-themesbun add sonner next-themes
Скопируйте и вставьте следующий код в свой проект.
"use client"
import {
CircleCheckIcon,
InfoIcon,
Loader2Icon,
OctagonXIcon,
TriangleAlertIcon,
} from "lucide-react"
import { useTheme } from "next-themes"
import { Toaster as Sonner, type ToasterProps } from "sonner"
const Toaster = ({ ...props }: ToasterProps) => {
const { theme = "system" } = useTheme()
return (
<Sonner
theme={theme as ToasterProps["theme"]}
className="toaster group"
icons={{
success: <CircleCheckIcon className="size-4" />,
info: <InfoIcon className="size-4" />,
warning: <TriangleAlertIcon className="size-4" />,
error: <OctagonXIcon className="size-4" />,
loading: <Loader2Icon className="size-4 animate-spin" />,
}}
toastOptions={{
// Fully unstyled so the neobrutalist recipe below owns every surface:
// hard offset shadow, 2px border, square corners, press-down buttons.
unstyled: true,
classNames: {
toast:
"group/toast relative flex w-(--width) items-center gap-3 rounded border-2 border-border bg-popover p-4 font-sans text-popover-foreground shadow-md",
content: "flex min-w-0 flex-col gap-0.5",
title: "font-head text-sm font-medium",
description: "text-sm text-muted-foreground",
icon: "shrink-0",
actionButton:
"ms-auto h-fit min-w-fit shrink-0 rounded border-2 border-border bg-primary px-2 py-1 text-xs font-medium text-primary-foreground shadow-sm transition-all duration-200 hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none",
cancelButton:
"ms-auto h-fit min-w-fit shrink-0 rounded border-2 border-border bg-muted px-2 py-1 text-xs font-medium text-foreground shadow-sm transition-all duration-200 hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none",
closeButton:
"absolute -top-2 -start-2 rounded-full border-2 border-border bg-background p-0.5 transition-colors hover:bg-muted",
success: "[&_[data-icon]]:text-chart-2",
warning: "[&_[data-icon]]:text-chart-1",
error: "[&_[data-icon]]:text-destructive",
},
}}
{...props}
/>
)
}
export { Toaster }
Добавьте компонент Toaster.
import { Toaster } from "@/components/ui/sonner"
export default function RootLayout({ children }) {
return (
<html lang="en">
<head />
<body>
<Toaster />
<main>{children}</main>
</body>
</html>
)
}Использование#
import { toast } from "sonner"toast("Event has been created.")Примеры#
Типы#
"use client"
import { toast } from "sonner"Описание#
"use client"
import { toast } from "sonner"Позиция#
Используйте свойство position, чтобы изменить позицию toast.
"use client"
import { toast } from "sonner"Доступность#
Отдельного WAI-ARIA APG-паттерна для toast'ов нет; ближайшее руководство — паттерн Alert. Sonner рендерит toast'ы в polite ARIA live region, поэтому программы чтения с экрана объявляют их, не крадя фокус. Region вне обычного tab order — до него добираются только hotkey — а таймеры dismiss встают на паузу, пока стопка в hover, в фокусе или вкладка скрыта.
Клавиатурные взаимодействия:
| Клавиша | Действие |
|---|---|
Alt + T | Сфокусировать и развернуть стопку toast (настраивается через проп hotkey у Toaster) |
Tab / Shift + Tab | Перемещаться по toast'ам и их action / close-кнопкам |
Enter / Space | Активировать action или close-кнопку в фокусе |
Escape | Свернуть стопку toast, пока фокус внутри неё |
Справочник API#
Дополнительную информацию см. в справочнике API Sonner.