Skip to content

Feedback Components

Feedback components communicate system status to users — success messages, loading indicators, error states, and empty placeholders.

Package: svelte-sonner ^1.1.0

UI wrapper: $lib/components/ui/sonner

The Sonner component (the toast container) is mounted once at the root layout. It automatically adapts to the current color mode via mode-watcher:

<!-- Already configured in the root layout -->
<script lang="ts">
import { Sonner } from "$lib/components/ui/sonner";
</script>
<Sonner />

The wrapper provides custom icons for each toast type using Lucide icons:

TypeIcon
LoadingLoader2Icon (animated spin)
SuccessCircleCheckIcon
ErrorOctagonXIcon
InfoInfoIcon
WarningTriangleAlertIcon

Import the toast function directly from svelte-sonner anywhere in your code:

<script lang="ts">
import { toast } from "svelte-sonner";
async function handleSave() {
toast.loading("Saving changes...");
try {
await saveData();
toast.success("Changes saved successfully");
} catch (error) {
toast.error("Failed to save changes");
}
}
</script>
// Simple messages
toast.success("Profile updated");
toast.error("Something went wrong");
toast.info("New feature available");
toast.warning("Your session will expire soon");

Import: $lib/modules/shared

The LoadingSkeleton component renders animated placeholder shapes that match common content layouts. It uses the Skeleton primitive from $lib/components/ui/skeleton.

PropTypeDefaultDescription
variant"card" | "post" | "chat" | "profile""card"Layout pattern to render
countnumber3Number of skeleton items to show

Renders rounded bordered containers with three lines of varying width. Best for generic list items, settings panels, or card grids.

<LoadingSkeleton variant="card" count={3} />
<script lang="ts">
import { LoadingSkeleton } from "$lib/modules/shared";
let { data } = $props();
</script>
{#if data.loading}
<LoadingSkeleton variant="post" count={5} />
{:else}
<!-- Render actual content -->
{/if}

Import: $lib/modules/shared

The EmptyState component displays a centered placeholder when a list or page has no content to show.

PropTypeRequiredDescription
iconComponent<{ class?: string }>NoSvelte component to render as the icon
titlestringYesPrimary message
descriptionstringNoSecondary explanatory text
classstringNoAdditional CSS classes
<script lang="ts">
import { EmptyState } from "$lib/modules/shared";
import InboxIcon from "@tabler/icons-svelte/icons/inbox";
</script>
{#if messages.length === 0}
<EmptyState
icon={InboxIcon}
title="No messages yet"
description="Start a conversation to see messages here."
/>
{/if}

The component renders with py-16 vertical padding and centers all content. The icon (if provided) renders at size-10 in text-muted-foreground, with the title in text-lg font-semibold and description in text-sm text-muted-foreground constrained to max-w-xs.


Import: $lib/modules/shared

The ConfirmationModal provides a pre-built dialog for destructive or important actions that require explicit user consent.

<script lang="ts">
import { ConfirmationModal } from "$lib/modules/shared";
let showDeleteConfirm = $state(false);
function handleDelete() {
// perform deletion
showDeleteConfirm = false;
}
</script>
<ConfirmationModal
bind:open={showDeleteConfirm}
title="Delete Post"
description="This action cannot be undone. The post and all its comments will be permanently deleted."
onConfirm={handleDelete}
/>

Honeycomb does not have a single global error display component. Instead, errors are handled through a combination of strategies:

For form submissions and API calls, use toast.error():

try {
await api.updateProfile(formData);
toast.success("Profile updated");
} catch (e) {
toast.error("Failed to update profile");
}

For route-level errors, SvelteKit’s built-in +error.svelte pages handle 404s and server errors.

Form fields use aria-invalid attributes which the UI primitives style automatically with a destructive ring:

<Input type="email" aria-invalid={!!errors.email} />
{#if errors.email}
<p class="text-sm text-destructive">{errors.email}</p>
{/if}

NeedComponent / FunctionImport
Success/error notificationstoastsvelte-sonner
Loading placeholdersLoadingSkeleton$lib/modules/shared
Basic skeleton shapesSkeleton$lib/components/ui/skeleton
No-content placeholderEmptyState$lib/modules/shared
Destructive action confirmationConfirmationModal$lib/modules/shared
Form field errorsaria-invalid + inline textBuilt-in to UI primitives