Saltar al contenido

Sonner

Notificaciones en forma de tostadas apiladas para confirmaciones de guardado, subidas de archivos y ventanas de deshacer, con ese estilo neobrutalista de bordes y sombras.

"use client"

import { toast } from "sonner"

Sonner pone en pantalla toasts apilados que se descartan solos a partir de una única llamada a toast(): monta un <Toaster /> y lanza notificaciones desde cualquier parte, sin cablear ningún contexto. No es una primitiva de Radix ni de Base UI: ambas variantes de backend envuelven la misma biblioteca Sonner, de emilkowalski, y siguen la receta neobrutalista: bordes gruesos, sombras marcadas y tipografía en negrita.

Recurre a este componente cuando:

  • Confirmar acciones — «Guardado», «Copiado al portapapeles», «Ajustes actualizados» cuando se resuelve una mutación.
  • Operaciones asíncronastoast.promise() sigue una subida o un despliegue desde la carga hasta el éxito o el error en un único toast.
  • Ventanas de deshacer — eliminaciones y archivados con un botón de acción, para que las operaciones destructivas tengan un periodo de gracia.

Acerca de

Sonner está desarrollado y mantenido por emilkowalski.

Instalación

Ejecuta el siguiente comando:

pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/sonner.json
npx shadcn@latest add https://neobrutalism.com/r/base/sonner.json
yarn dlx shadcn@latest add https://neobrutalism.com/r/base/sonner.json
bunx --bun shadcn@latest add https://neobrutalism.com/r/base/sonner.json

Añade el componente Toaster.

app/layout.tsx
import { Toaster } from "@/components/ui/sonner"
 
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <head />
      <body>
        <main>{children}</main>
        <Toaster />
      </body>
    </html>
  )
}

Instala las siguientes dependencias:

pnpm add sonner next-themes
npm install sonner next-themes
yarn add sonner next-themes
bun add sonner next-themes

Copia y pega el siguiente código en tu proyecto.

components/ui/sonner.tsx
"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 }

Añade el componente Toaster.

app/layout.tsx
import { Toaster } from "@/components/ui/sonner"
 
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <head />
      <body>
        <Toaster />
        <main>{children}</main>
      </body>
    </html>
  )
}

Uso

import { toast } from "sonner"
toast("Event has been created.")

Ejemplos

Tipos

"use client"

import { toast } from "sonner"

Descripción

"use client"

import { toast } from "sonner"

Posición

Usa la prop position para cambiar la posición del toast.

"use client"

import { toast } from "sonner"

Accesibilidad

No existe un patrón APG de WAI-ARIA dedicado a los toasts; la guía más cercana es el patrón Alert. Sonner renderiza los toasts en una región viva ARIA de cortesía (polite), así que los lectores de pantalla los anuncian sin robar el foco. La región queda fuera del orden de tabulación normal (solo se alcanza mediante el atajo) y los temporizadores de descarte se pausan mientras la pila está bajo el cursor, tiene el foco o la pestaña está oculta.

Interacciones de teclado:

TeclaAcción
Alt + TEnfoca y expande la pila de toasts (configurable con la prop hotkey de Toaster)
Tab / Shift + TabRecorre los toasts y sus botones de acción y de cierre
Enter / SpaceActiva el botón de acción o de cierre con foco
EscapeContrae la pila de toasts mientras el foco está dentro

Referencia de API

Consulta la referencia de API de Sonner para obtener más información.