Toaster
The area where toasts appear, which also turns Salla SDK messages into toasts; TwilightProvider already renders one for you.
import { Toaster, ToastPosition } from '@salla.sa/twilight-theme-engine/components/toast';In plain words
Toasts need a place on the page to appear. Toaster is that place, and TwilightProvider renders it for every theme, so most themes never write <Toaster />.
It also catches the messages the Salla SDK shows by itself, such as the one after a product is added to the cart, and shows them as toasts in the same style.
What you usually change are the provider's props: toastMobilePosition (where toasts go on phones) and addToCartToast (whether the "added to cart" toast shows).
Signature
function Toaster(props: {
mobilePosition?: ToastPosition; // default 'bottom-center', used below 768px wide
addToCartToast?: boolean; // default true
}): JSX.Element
type ToastPosition =
| 'top-left' | 'top-right'
| 'bottom-left' | 'bottom-right'
| 'top-center' | 'bottom-center';
// How a theme configures the one TwilightProvider renders:
<TwilightProvider
toast={true} // default: render the Toaster
toastMobilePosition="bottom-center"
addToCartToast={true}
/>Try it live
import { getSallaSDK } from '@salla.sa/twilight-theme-engine/utils';
// Nothing to mount: TwilightProvider already renders the Toaster.
export function CopyCouponButton({ code }: { code: string }) {
const copy = async () => {
await navigator.clipboard.writeText(code);
getSallaSDK()?.notify.success('Coupon copied');
};
return (
<button type="button" className="btn btn--outline-primary" onClick={copy}>
{code}
</button>
);
}
Example
import { HeadContent, Outlet, Scripts } from '@tanstack/react-router';
import { TwilightProvider } from '@salla.sa/twilight-theme-engine';
import {
createTwilightRootRoute,
getTwilightContext,
} from '@salla.sa/twilight-theme-engine/tanstack';
import themeTranslations from 'virtual:twilight/theme-translations';
export const Route = createTwilightRootRoute()({ shellComponent: RootComponent });
function RootComponent() {
const ctx = getTwilightContext();
return (
<html lang={ctx.locale} dir={ctx.dir} suppressHydrationWarning>
<head>
<HeadContent />
</head>
<body suppressHydrationWarning>
<TwilightProvider
translations={themeTranslations}
// A bottom navigation bar would cover bottom-center toasts on phones.
toastMobilePosition="top-center"
// The cart badge reacts to adds, so skip the default "added to cart" toast.
addToCartToast={false}
>
<Outlet />
</TwilightProvider>
<Scripts />
</body>
</html>
);
}
How it behaves
TwilightProviderrenders it inside<Suspense>once the store is ready, with itstoastMobilePositionandaddToCartToastprops, unless you passtoast={false}. It wraps sonner'sToasterand needsuseTwilight(), so it must sit inside the provider.Desktop position is fixed:
top-lefton right-to-left pages,top-rightotherwise (fromtheme.is_rtl). Below 768px wide it usesmobilePosition. The choice is re-made on windowresizeonly.On mount it calls
Salla.notify.setNotifier(…)ifwindow.Salla.notifyexists at that moment. From then on every SDK message becomes a toast: a string message is the title, an object usestitleortext(andiconas the type);success,errorandwarningkeep their type, anything else isinfo; all last 3 seconds.It does not wait for the SDK: the effect runs once (it re-runs only when
addToCartToastchanges). If the SDK has not set upSalla.notifyby then, SDK messages keep the SDK's own default notifier, which is the browser'salert().The add-to-cart message (SDK data
data.googleTags.event === 'addToCart') is skipped whenaddToCartToastisfalseor the theme settingenable_add_product_toastis on, because a theme with that setting renders its own richer toast.Inside the Salla mobile app (
Salla.mobile.isEnabled()), SDK messages go to the app's native alert through theui::alertevent instead of a toast.Flash messages: a
?success=…or?danger=…query parameter present when it mounts is shown once as a success or error toast, and removed from the address withhistory.replaceState.Styles are Tailwind classes (a white card with
border-2colored by type,rounded-xl,font-primary), top offsets add--app-safe-area-top, and the font isvar(--font-main). On unmount it sets the SDK notifier to a function that does nothing.
Gotchas
Rendering your own
<Toaster />while the provider's is on shows every toast twice: neither passes a sonnerid, and sonner shows id-less toasts in every Toaster. The one mounted last also takes overSalla.notify, and when it unmounts it sets the notifier to a no-op, so SDK messages silently stop until the page reloads (the provider's Toaster does not register again). Render your own only withtoast={false}on the provider.toast={false}with no Toaster of your own meanstoast(…)calls show nothing, and the SDK falls back to its built-in notifier, which is the browser'salert()dialog.
Related
Source and docs
- Engine source:
packages/theme-engine/src/components/toast/Toaster.tsx