Skip to content
Guides

Styling

From a class on one toast to owning every pixel: pick the lightest tool that does the job.

  1. Classes — class, descriptionClass and classes, per toast or for all of them through toastOptions.
  2. Inline styles — style, actionButtonStyle, cancelButtonStyle.
  3. CSS variables — recolor the built-in theme.
  4. Unstyled — keep behavior, drop the look for some toasts.
  5. Headless entry — no stylesheet at all.

Classes

<Toaster
  toastOptions={{
    class: "my-toast",
    descriptionClass: "my-description",
    classes: {
      title: "my-title",
      actionButton: "my-action",
      error: "my-error-toast",
    },
  }}
/>

// …or per toast
toast("Saved", { class: "my-toast", classes: { title: "font-semibold" } });
classes keyTypeDefault
toastClassList—
The toast <li>.
titleClassList—
The title.
descriptionClassList—
The description.
contentClassList—
Wrapper of title and description.
iconClassList—
Icon wrapper.
loaderClassList—
The loading spinner.
actionButtonClassList—
Action button.
cancelButtonClassList—
Cancel button.
closeButtonClassList—
Close button.
success, info, warning, error, loadingClassList—
Added to the toast <li> when it has that type.
defaultClassList—
Added to the toast <li> when it has no type: toast(), toast.message() and toast.custom() (same as sonner).

Values are Qwik ClassLists — a string, an array or an object of booleans all work.

Inline styles

<Toaster
  toastOptions={{
    style: { background: "hsl(270 70% 96%)", borderColor: "hsl(270 60% 85%)" },
    actionButtonStyle: { background: "hsl(270 60% 50%)" },
  }}
/>

CSS variables

The stylesheet reads its colors from custom properties set per theme on the toast list ([data-sonner-toaster][data-sonner-theme="light"]). Override them with a more specific selector, so the result doesn't depend on stylesheet order:

/* The leading "html" outranks the library's own selectors */
html [data-sonner-toaster][data-sonner-theme] {
  --border-radius: 12px;
}

html [data-sonner-toaster][data-sonner-theme="light"] {
  --normal-bg: hsl(40 30% 98%);
  --normal-border: hsl(40 20% 88%);
  --normal-text: hsl(30 10% 15%);
}
VariableTypeDefault
--normal-bg / --normal-border / --normal-textcolor—
Colors of a regular toast for the current theme.
--success-bg / -border / -textcolor—
Rich colors of success toasts. Same trio for --info-*, --warning-* and --error-*.
--border-radiuslength8px
Toast corner radius.
--widthlength356px
Toast width (set on the list).
--gaplength14px
Space between expanded toasts — prefer the gap prop.
--offset-{top,right,bottom,left}length—
Viewport offsets; set them with the offset / mobileOffset props.
--gray1 … --gray12color—
Gray scale used by borders, the close button and the spinner.

Unstyled toasts

unstyled: true removes the default box (data-styled="false") but keeps positioning, stacking, swiping and timers — a good fit for utility classes:

<Toaster
  toastOptions={{
    unstyled: true,
    classes: {
      toast: "flex w-[356px] items-center gap-3 rounded-xl bg-zinc-900 p-4 text-sm text-white",
      title: "font-medium",
      description: "text-zinc-400",
      actionButton: "ml-auto rounded-md bg-white px-2 py-1 text-xs text-zinc-900",
    },
  }}
/>

Headless entry

qwik-sonner/headless exports the same Toaster, toast and useSonner, plus the inner Toast component and the layout constants (TOAST_WIDTH, GAP, VIEWPORT_OFFSET, …). It renders the same markup without injecting any CSS.

import { Toaster, toast } from "qwik-sonner/headless";
import styles from "./toasts.css?inline";

export const AppToaster = component$(() => {
  useStyles$(styles); // your own stylesheet, targeting the data-sonner-* attributes
  return <Toaster />;
});

Top layer with the headless entry

The styled entry resets the browser's default [popover] box. Without it, add the reset yourself when you use topLayer:

/* Only needed with the headless entry + topLayer */
[data-sonner-toaster-popover]:popover-open {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  overflow: visible;
  pointer-events: none;
}

[data-sonner-toaster-popover] [data-sonner-toaster] {
  pointer-events: auto;
}

Data attributes

The markup follows sonner 2's data-sonner-* contract, so styles written for sonner carry over. Use the attributes as styling hooks or test selectors.

SelectorTypeDefault
[data-sonner-toaster]<ol>—
A toast list, one per position in use. Also carries data-sonner-theme, data-x-position, data-y-position and dir.
[data-sonner-toast]<li>—
A toast. State attributes: data-type, data-styled, data-mounted, data-removed, data-visible, data-front, data-index, data-expanded, data-swiping, data-swipe-out, data-swipe-direction, data-dismissible, data-rich-colors, data-invert, data-promise.
[data-icon] [data-content] [data-title] [data-description]<div>—
The toast's parts.
[data-button] [data-action] [data-cancel] [data-close-button]<button>—
Buttons inside a toast.
[data-sonner-toaster-popover]<div>—
Present when topLayer is on; the element promoted to the top layer.