Feedback & Loading

Alerts, toasts, spinners, and loading state components.

Show user feedback with Alert for inline messages, toast (Sonner) for transient notifications, Spinner for action indicators, Progress for determinate progress, LoadingOverlay/GlobalLoader for blocking states, and Tooltip for hover hints. Add Toaster to your root layout once.

This guide is part of the UI Components documentation.

Feedback and loading components communicate system status to users - from error messages and success confirmations to progress indicators and loading states - ensuring users always know what's happening.

Use Alert when: displaying persistent messages that users need to read (errors, warnings, info).

  • Use toast when: confirming transient actions (save successful, item deleted).
  • Use Spinner/Progress when: operations take >300ms.
  • If unsure: toast for user-triggered actions, Alert for system-initiated messages.

Alert

Alert message for important information.

import { Alert, AlertTitle, AlertDescription } from '@kit/ui/alert';
<Alert>
<AlertTitle>Information</AlertTitle>
<AlertDescription>
This is an informational alert message.
</AlertDescription>
</Alert>
<Alert variant="destructive">
<AlertTitle>Error</AlertTitle>
<AlertDescription>
Something went wrong. Please try again.
</AlertDescription>
</Alert>

With Icon

import { TriangleAlert, CheckCircle } from 'lucide-react';
<Alert variant="destructive">
<TriangleAlert className="h-4 w-4" />
<AlertTitle>Error</AlertTitle>
<AlertDescription>Operation failed.</AlertDescription>
</Alert>
<Alert>
<CheckCircle className="h-4 w-4" />
<AlertTitle>Success</AlertTitle>
<AlertDescription>Operation completed.</AlertDescription>
</Alert>

Toast (Sonner)

Toast notifications using Sonner.

import { toast } from '@kit/ui/sonner';
// Simple toasts
toast.success('Operation successful');
toast.error('Something went wrong');
toast.info('New message received');
toast.warning('Please review your input');
// With description
toast.success('Saved', {
description: 'Your changes have been saved.',
});
// Promise-based toast
await toast.promise(saveData(), {
loading: 'Saving...',
success: 'Data saved!',
error: 'Failed to save',
});

Toaster Setup

Add to your root layout:

import { Toaster } from '@kit/ui/sonner';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Toaster />
</body>
</html>
);
}

Spinner

Loading spinner animation.

import { Spinner } from '@kit/ui/spinner';
<Spinner />
<Spinner className="h-8 w-8" />
<Spinner className="h-4 w-4 text-primary" />

In Button

<Button disabled={isPending}>
{isPending ? (
<>
<Spinner className="mr-2 h-4 w-4" />
Loading...
</>
) : (
'Submit'
)}
</Button>

Progress

Progress bar indicator.

import { Progress } from '@kit/ui/progress';
<Progress value={33} />
<Progress value={66} />
<Progress value={100} />

Loading Overlay

Semi-transparent overlay with spinner.

import { LoadingOverlay } from '@kit/ui/loading-overlay';
<div className="relative">
<Content />
{isLoading && <LoadingOverlay />}
</div>

Global Loader

Full-page loading state.

import { GlobalLoader } from '@kit/ui/global-loader';
<GlobalLoader />
{/* With options */}
<GlobalLoader
displayLogo
fullPage
displaySpinner
displayTopLoadingBar
/>

Tooltip

Hover tooltip for additional information.

import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@kit/ui/tooltip';
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Button variant="outline" size="icon">
<HelpCircle className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Helpful information here</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>

In MakerKit, we use toast.promise() heavily for Server Action feedback - it handles loading, success, and error states in one call, which users find clearer than separate loading spinners.

Common Pitfalls

  • Missing Toaster in layout: Toast won't appear without <Toaster /> in your root layout. Add it once and it works everywhere.
  • Alert vs Toast confusion: Use Alert for persistent, inline messages. Use toast for transient notifications that auto-dismiss.
  • Tooltip without TooltipProvider: Tooltips need TooltipProvider as a parent. Wrap your app or component tree once.
  • Spinner without sizing: Spinner needs explicit dimensions via className. Default is small - use className="h-8 w-8" for visibility.
  • LoadingOverlay without relative parent: LoadingOverlay uses absolute positioning. Its parent container needs position: relative.
  • Progress value out of range: Progress expects value 0-100. Values outside this range may cause visual glitches.

Frequently Asked Questions

How do I show a toast from a Server Action?
Server Actions can't call toast directly. Return success/error status and call toast in the client component that invoked the action.
Can I customize toast duration?
Yes. Pass duration in milliseconds: toast.success('Saved', { duration: 5000 }). Default is typically 4 seconds.
What's the difference between LoadingOverlay and GlobalLoader?
LoadingOverlay covers a specific container (relative positioned). GlobalLoader covers the entire viewport for full-page loading states.
How do I show loading state during navigation?
Use GlobalLoader with displayTopLoadingBar prop for a thin progress bar. Combine with Next.js loading.tsx for route transitions.
Can I use Alert in Server Components?
Yes. Alert is a static display component with no client-side interactivity, so it works in Server Components without 'use client'.

Next: Utilities →