From 77bf7d28b4c8154bc6dc59dd449dcf06638f0f34 Mon Sep 17 00:00:00 2001 From: Jakob Guddas Date: Mon, 13 Oct 2025 20:16:50 +0200 Subject: [PATCH] feat(components): changed sonner defaults to use lucide icons (#7620) * feat(components): changed sonner defaults to use lucide icons * Update new-york-v4 sonner.tsx * fix: icons and docs * fix * fix --------- Co-authored-by: shadcn --- apps/v4/content/docs/components/sonner.mdx | 57 ++++++++++++++++- apps/v4/public/r/styles/default/sonner.json | 2 +- .../public/r/styles/new-york-v4/registry.json | 13 ++++ .../r/styles/new-york-v4/sonner-types.json | 15 +++++ .../public/r/styles/new-york-v4/sonner.json | 2 +- apps/v4/registry.json | 13 ++++ apps/v4/registry/__index__.tsx | 18 ++++++ .../new-york-v4/examples/sonner-types.tsx | 61 +++++++++++++++++++ apps/v4/registry/new-york-v4/ui/sonner.tsx | 14 +++++ apps/v4/registry/registry-examples.ts | 11 ++++ apps/www/public/r/styles/default/sonner.json | 2 +- .../public/r/styles/new-york-v4/registry.json | 13 ++++ .../r/styles/new-york-v4/sonner-types.json | 15 +++++ .../public/r/styles/new-york-v4/sonner.json | 2 +- apps/www/registry/default/ui/form.tsx | 4 +- apps/www/registry/default/ui/sonner.tsx | 14 +++++ apps/www/registry/new-york/ui/form.tsx | 4 +- 17 files changed, 250 insertions(+), 10 deletions(-) create mode 100644 apps/v4/public/r/styles/new-york-v4/sonner-types.json create mode 100644 apps/v4/registry/new-york-v4/examples/sonner-types.tsx create mode 100644 apps/www/public/r/styles/new-york-v4/sonner-types.json diff --git a/apps/v4/content/docs/components/sonner.mdx b/apps/v4/content/docs/components/sonner.mdx index d7a761dbb..3c73bc756 100644 --- a/apps/v4/content/docs/components/sonner.mdx +++ b/apps/v4/content/docs/components/sonner.mdx @@ -68,7 +68,7 @@ npm install sonner next-themes Add the Toaster component -```tsx title="app/layout.tsx" {1,9} +```tsx showLineNumbers title="app/layout.tsx" {1,8} import { Toaster } from "@/components/ui/sonner" export default function RootLayout({ children }) { @@ -76,8 +76,8 @@ export default function RootLayout({ children }) { -
{children}
+
{children}
) @@ -99,3 +99,56 @@ import { toast } from "sonner" ```tsx toast("Event has been created.") ``` + +## Examples + + + +## Changelog + +### 2025-10-13 Icons + +We've updated the Sonner component to use icons from `lucide`. Update your `sonner.tsx` file to use the new icons. + +```tsx showLineNumbers title="components/ui/sonner.tsx" {3-9,20-26} +"use client" + +import { + CircleCheckIcon, + InfoIcon, + Loader2Icon, + OctagonXIcon, + TriangleAlertIcon, +} from "lucide-react" +import { useTheme } from "next-themes" +import { Toaster as Sonner, ToasterProps } from "sonner" + +const Toaster = ({ ...props }: ToasterProps) => { + const { theme = "system" } = useTheme() + + return ( + , + info: , + warning: , + error: , + loading: , + }} + style={ + { + "--normal-bg": "var(--popover)", + "--normal-text": "var(--popover-foreground)", + "--normal-border": "var(--border)", + "--border-radius": "var(--radius)", + } as React.CSSProperties + } + {...props} + /> + ) +} + +export { Toaster } +``` diff --git a/apps/v4/public/r/styles/default/sonner.json b/apps/v4/public/r/styles/default/sonner.json index 88ac39ce6..80bf2c577 100644 --- a/apps/v4/public/r/styles/default/sonner.json +++ b/apps/v4/public/r/styles/default/sonner.json @@ -10,7 +10,7 @@ "files": [ { "path": "ui/sonner.tsx", - "content": "\"use client\"\n\nimport { useTheme } from \"next-themes\"\nimport { Toaster as Sonner } from \"sonner\"\n\ntype ToasterProps = React.ComponentProps\n\nconst Toaster = ({ ...props }: ToasterProps) => {\n const { theme = \"system\" } = useTheme()\n\n return (\n \n )\n}\n\nexport { Toaster }\n", + "content": "\"use client\"\n\nimport {\n CircleCheck,\n Info,\n LoaderCircle,\n OctagonX,\n TriangleAlert,\n} from \"lucide-react\"\nimport { useTheme } from \"next-themes\"\nimport { Toaster as Sonner } from \"sonner\"\n\ntype ToasterProps = React.ComponentProps\n\nconst Toaster = ({ ...props }: ToasterProps) => {\n const { theme = \"system\" } = useTheme()\n\n return (\n ,\n info: ,\n warning: ,\n error: ,\n loading: ,\n }}\n toastOptions={{\n classNames: {\n toast:\n \"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg\",\n description: \"group-[.toast]:text-muted-foreground\",\n actionButton:\n \"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground\",\n cancelButton:\n \"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground\",\n },\n }}\n {...props}\n />\n )\n}\n\nexport { Toaster }\n", "type": "registry:ui", "target": "" } diff --git a/apps/v4/public/r/styles/new-york-v4/registry.json b/apps/v4/public/r/styles/new-york-v4/registry.json index 4d17f6587..2d729b812 100644 --- a/apps/v4/public/r/styles/new-york-v4/registry.json +++ b/apps/v4/public/r/styles/new-york-v4/registry.json @@ -6863,6 +6863,19 @@ } ] }, + { + "name": "sonner-types", + "type": "registry:example", + "registryDependencies": [ + "sonner" + ], + "files": [ + { + "path": "registry/new-york-v4/examples/sonner-types.tsx", + "type": "registry:example" + } + ] + }, { "name": "spinner-demo", "type": "registry:example", diff --git a/apps/v4/public/r/styles/new-york-v4/sonner-types.json b/apps/v4/public/r/styles/new-york-v4/sonner-types.json new file mode 100644 index 000000000..05941a138 --- /dev/null +++ b/apps/v4/public/r/styles/new-york-v4/sonner-types.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "sonner-types", + "type": "registry:example", + "registryDependencies": [ + "sonner" + ], + "files": [ + { + "path": "registry/new-york-v4/examples/sonner-types.tsx", + "content": "\"use client\"\n\nimport { toast } from \"sonner\"\n\nimport { Button } from \"@/registry/new-york-v4/ui/button\"\n\nexport default function SonnerTypes() {\n return (\n
\n \n toast.success(\"Event has been created\")}\n >\n Success\n \n \n toast.info(\"Be at the area 10 minutes before the event time\")\n }\n >\n Info\n \n \n toast.warning(\"Event start time cannot be earlier than 8am\")\n }\n >\n Warning\n \n toast.error(\"Event has not been created\")}\n >\n Error\n \n {\n const promise = () =>\n new Promise((resolve) =>\n setTimeout(() => resolve({ name: \"Event\" }), 2000)\n )\n\n toast.promise(promise(), {\n loading: \"Loading...\",\n success: (data) => `${data.name} has been created`,\n error: \"Error\",\n })\n }}\n >\n Promise\n \n
\n )\n}\n", + "type": "registry:example" + } + ] +} \ No newline at end of file diff --git a/apps/v4/public/r/styles/new-york-v4/sonner.json b/apps/v4/public/r/styles/new-york-v4/sonner.json index 6e63f434c..d22159c96 100644 --- a/apps/v4/public/r/styles/new-york-v4/sonner.json +++ b/apps/v4/public/r/styles/new-york-v4/sonner.json @@ -9,7 +9,7 @@ "files": [ { "path": "registry/new-york-v4/ui/sonner.tsx", - "content": "\"use client\"\n\nimport { useTheme } from \"next-themes\"\nimport { Toaster as Sonner, ToasterProps } from \"sonner\"\n\nconst Toaster = ({ ...props }: ToasterProps) => {\n const { theme = \"system\" } = useTheme()\n\n return (\n \n )\n}\n\nexport { Toaster }\n", + "content": "\"use client\"\n\nimport {\n CircleCheckIcon,\n InfoIcon,\n Loader2Icon,\n OctagonXIcon,\n TriangleAlertIcon,\n} from \"lucide-react\"\nimport { useTheme } from \"next-themes\"\nimport { Toaster as Sonner, ToasterProps } from \"sonner\"\n\nconst Toaster = ({ ...props }: ToasterProps) => {\n const { theme = \"system\" } = useTheme()\n\n return (\n ,\n info: ,\n warning: ,\n error: ,\n loading: ,\n }}\n style={\n {\n \"--normal-bg\": \"var(--popover)\",\n \"--normal-text\": \"var(--popover-foreground)\",\n \"--normal-border\": \"var(--border)\",\n \"--border-radius\": \"var(--radius)\",\n } as React.CSSProperties\n }\n {...props}\n />\n )\n}\n\nexport { Toaster }\n", "type": "registry:ui" } ] diff --git a/apps/v4/registry.json b/apps/v4/registry.json index 4d17f6587..2d729b812 100644 --- a/apps/v4/registry.json +++ b/apps/v4/registry.json @@ -6863,6 +6863,19 @@ } ] }, + { + "name": "sonner-types", + "type": "registry:example", + "registryDependencies": [ + "sonner" + ], + "files": [ + { + "path": "registry/new-york-v4/examples/sonner-types.tsx", + "type": "registry:example" + } + ] + }, { "name": "spinner-demo", "type": "registry:example", diff --git a/apps/v4/registry/__index__.tsx b/apps/v4/registry/__index__.tsx index 3fadf601d..34d41e72b 100644 --- a/apps/v4/registry/__index__.tsx +++ b/apps/v4/registry/__index__.tsx @@ -6956,6 +6956,24 @@ export const Index: Record = { categories: undefined, meta: undefined, }, + "sonner-types": { + name: "sonner-types", + description: "", + type: "registry:example", + registryDependencies: ["sonner"], + files: [{ + path: "registry/new-york-v4/examples/sonner-types.tsx", + type: "registry:example", + target: "" + }], + component: React.lazy(async () => { + const mod = await import("@/registry/new-york-v4/examples/sonner-types.tsx") + const exportName = Object.keys(mod).find(key => typeof mod[key] === 'function' || typeof mod[key] === 'object') || item.name + return { default: mod.default || mod[exportName] } + }), + categories: undefined, + meta: undefined, + }, "spinner-demo": { name: "spinner-demo", description: "", diff --git a/apps/v4/registry/new-york-v4/examples/sonner-types.tsx b/apps/v4/registry/new-york-v4/examples/sonner-types.tsx new file mode 100644 index 000000000..6934a733f --- /dev/null +++ b/apps/v4/registry/new-york-v4/examples/sonner-types.tsx @@ -0,0 +1,61 @@ +"use client" + +import { toast } from "sonner" + +import { Button } from "@/registry/new-york-v4/ui/button" + +export default function SonnerTypes() { + return ( +
+ + + + + + +
+ ) +} diff --git a/apps/v4/registry/new-york-v4/ui/sonner.tsx b/apps/v4/registry/new-york-v4/ui/sonner.tsx index fb3015af3..2ec7deff6 100644 --- a/apps/v4/registry/new-york-v4/ui/sonner.tsx +++ b/apps/v4/registry/new-york-v4/ui/sonner.tsx @@ -1,5 +1,12 @@ "use client" +import { + CircleCheckIcon, + InfoIcon, + Loader2Icon, + OctagonXIcon, + TriangleAlertIcon, +} from "lucide-react" import { useTheme } from "next-themes" import { Toaster as Sonner, ToasterProps } from "sonner" @@ -10,6 +17,13 @@ const Toaster = ({ ...props }: ToasterProps) => { , + info: , + warning: , + error: , + loading: , + }} style={ { "--normal-bg": "var(--popover)", diff --git a/apps/v4/registry/registry-examples.ts b/apps/v4/registry/registry-examples.ts index 246b041bb..b172d29f2 100644 --- a/apps/v4/registry/registry-examples.ts +++ b/apps/v4/registry/registry-examples.ts @@ -2016,6 +2016,17 @@ export const examples: Registry["items"] = [ }, ], }, + { + name: "sonner-types", + type: "registry:example", + registryDependencies: ["sonner"], + files: [ + { + path: "examples/sonner-types.tsx", + type: "registry:example", + }, + ], + }, { name: "spinner-demo", type: "registry:example", diff --git a/apps/www/public/r/styles/default/sonner.json b/apps/www/public/r/styles/default/sonner.json index 88ac39ce6..80bf2c577 100644 --- a/apps/www/public/r/styles/default/sonner.json +++ b/apps/www/public/r/styles/default/sonner.json @@ -10,7 +10,7 @@ "files": [ { "path": "ui/sonner.tsx", - "content": "\"use client\"\n\nimport { useTheme } from \"next-themes\"\nimport { Toaster as Sonner } from \"sonner\"\n\ntype ToasterProps = React.ComponentProps\n\nconst Toaster = ({ ...props }: ToasterProps) => {\n const { theme = \"system\" } = useTheme()\n\n return (\n \n )\n}\n\nexport { Toaster }\n", + "content": "\"use client\"\n\nimport {\n CircleCheck,\n Info,\n LoaderCircle,\n OctagonX,\n TriangleAlert,\n} from \"lucide-react\"\nimport { useTheme } from \"next-themes\"\nimport { Toaster as Sonner } from \"sonner\"\n\ntype ToasterProps = React.ComponentProps\n\nconst Toaster = ({ ...props }: ToasterProps) => {\n const { theme = \"system\" } = useTheme()\n\n return (\n ,\n info: ,\n warning: ,\n error: ,\n loading: ,\n }}\n toastOptions={{\n classNames: {\n toast:\n \"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg\",\n description: \"group-[.toast]:text-muted-foreground\",\n actionButton:\n \"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground\",\n cancelButton:\n \"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground\",\n },\n }}\n {...props}\n />\n )\n}\n\nexport { Toaster }\n", "type": "registry:ui", "target": "" } diff --git a/apps/www/public/r/styles/new-york-v4/registry.json b/apps/www/public/r/styles/new-york-v4/registry.json index 4d17f6587..2d729b812 100644 --- a/apps/www/public/r/styles/new-york-v4/registry.json +++ b/apps/www/public/r/styles/new-york-v4/registry.json @@ -6863,6 +6863,19 @@ } ] }, + { + "name": "sonner-types", + "type": "registry:example", + "registryDependencies": [ + "sonner" + ], + "files": [ + { + "path": "registry/new-york-v4/examples/sonner-types.tsx", + "type": "registry:example" + } + ] + }, { "name": "spinner-demo", "type": "registry:example", diff --git a/apps/www/public/r/styles/new-york-v4/sonner-types.json b/apps/www/public/r/styles/new-york-v4/sonner-types.json new file mode 100644 index 000000000..05941a138 --- /dev/null +++ b/apps/www/public/r/styles/new-york-v4/sonner-types.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "sonner-types", + "type": "registry:example", + "registryDependencies": [ + "sonner" + ], + "files": [ + { + "path": "registry/new-york-v4/examples/sonner-types.tsx", + "content": "\"use client\"\n\nimport { toast } from \"sonner\"\n\nimport { Button } from \"@/registry/new-york-v4/ui/button\"\n\nexport default function SonnerTypes() {\n return (\n
\n \n toast.success(\"Event has been created\")}\n >\n Success\n \n \n toast.info(\"Be at the area 10 minutes before the event time\")\n }\n >\n Info\n \n \n toast.warning(\"Event start time cannot be earlier than 8am\")\n }\n >\n Warning\n \n toast.error(\"Event has not been created\")}\n >\n Error\n \n {\n const promise = () =>\n new Promise((resolve) =>\n setTimeout(() => resolve({ name: \"Event\" }), 2000)\n )\n\n toast.promise(promise(), {\n loading: \"Loading...\",\n success: (data) => `${data.name} has been created`,\n error: \"Error\",\n })\n }}\n >\n Promise\n \n
\n )\n}\n", + "type": "registry:example" + } + ] +} \ No newline at end of file diff --git a/apps/www/public/r/styles/new-york-v4/sonner.json b/apps/www/public/r/styles/new-york-v4/sonner.json index 6e63f434c..d22159c96 100644 --- a/apps/www/public/r/styles/new-york-v4/sonner.json +++ b/apps/www/public/r/styles/new-york-v4/sonner.json @@ -9,7 +9,7 @@ "files": [ { "path": "registry/new-york-v4/ui/sonner.tsx", - "content": "\"use client\"\n\nimport { useTheme } from \"next-themes\"\nimport { Toaster as Sonner, ToasterProps } from \"sonner\"\n\nconst Toaster = ({ ...props }: ToasterProps) => {\n const { theme = \"system\" } = useTheme()\n\n return (\n \n )\n}\n\nexport { Toaster }\n", + "content": "\"use client\"\n\nimport {\n CircleCheckIcon,\n InfoIcon,\n Loader2Icon,\n OctagonXIcon,\n TriangleAlertIcon,\n} from \"lucide-react\"\nimport { useTheme } from \"next-themes\"\nimport { Toaster as Sonner, ToasterProps } from \"sonner\"\n\nconst Toaster = ({ ...props }: ToasterProps) => {\n const { theme = \"system\" } = useTheme()\n\n return (\n ,\n info: ,\n warning: ,\n error: ,\n loading: ,\n }}\n style={\n {\n \"--normal-bg\": \"var(--popover)\",\n \"--normal-text\": \"var(--popover-foreground)\",\n \"--normal-border\": \"var(--border)\",\n \"--border-radius\": \"var(--radius)\",\n } as React.CSSProperties\n }\n {...props}\n />\n )\n}\n\nexport { Toaster }\n", "type": "registry:ui" } ] diff --git a/apps/www/registry/default/ui/form.tsx b/apps/www/registry/default/ui/form.tsx index c7cdffff7..c8e5e898a 100644 --- a/apps/www/registry/default/ui/form.tsx +++ b/apps/www/registry/default/ui/form.tsx @@ -24,7 +24,7 @@ type FormFieldContextValue< name: TName } -const FormFieldContext = React.createContext(null); +const FormFieldContext = React.createContext(null) const FormField = < TFieldValues extends FieldValues = FieldValues, @@ -70,7 +70,7 @@ type FormItemContextValue = { id: string } -const FormItemContext = React.createContext(null) +const FormItemContext = React.createContext(null) const FormItem = React.forwardRef< HTMLDivElement, diff --git a/apps/www/registry/default/ui/sonner.tsx b/apps/www/registry/default/ui/sonner.tsx index 452f4d9f0..6e6fd25c2 100644 --- a/apps/www/registry/default/ui/sonner.tsx +++ b/apps/www/registry/default/ui/sonner.tsx @@ -1,5 +1,12 @@ "use client" +import { + CircleCheck, + Info, + LoaderCircle, + OctagonX, + TriangleAlert, +} from "lucide-react" import { useTheme } from "next-themes" import { Toaster as Sonner } from "sonner" @@ -12,6 +19,13 @@ const Toaster = ({ ...props }: ToasterProps) => { , + info: , + warning: , + error: , + loading: , + }} toastOptions={{ classNames: { toast: diff --git a/apps/www/registry/new-york/ui/form.tsx b/apps/www/registry/new-york/ui/form.tsx index 2573cc6c5..a9d6d74ae 100644 --- a/apps/www/registry/new-york/ui/form.tsx +++ b/apps/www/registry/new-york/ui/form.tsx @@ -24,7 +24,7 @@ type FormFieldContextValue< name: TName } -const FormFieldContext = React.createContext(null); +const FormFieldContext = React.createContext(null) const FormField = < TFieldValues extends FieldValues = FieldValues, @@ -70,7 +70,7 @@ type FormItemContextValue = { id: string } -const FormItemContext = React.createContext(null) +const FormItemContext = React.createContext(null) const FormItem = React.forwardRef< HTMLDivElement,