Component Library
Overview
We use Shadcn/ui for Svelte - components are copied into the project (not installed as dependencies), allowing full customization.
Components are in: src/lib/components/ui/
Button
From user-integration.svelte:
import { Button } from '$lib/components/ui/button';
// Default variant
<Button onclick={handleClick}>
Click Me
</Button>
// Outline variant
<Button variant="outline" onclick={handleClick}>
View Details
</Button>
// Destructive (red) variant
<Button variant="destructive" onclick={handleRemove}>
Remove Integration
</Button>
// Disabled state
<Button disabled={isLoading}>
{isLoading ? 'Loading...' : 'Submit'}
</Button>
// With icon
<Button variant="outline" class="gap-2">
<Eye class="h-4 w-4" />
View Details
</Button>
Variants: default, destructive, outline, secondary, ghost, link
Sidebar
From Sidebar.svelte:
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarMenu,
SidebarMenuItem,
SidebarMenuButton,
} from '$lib/components/ui/sidebar';
<Sidebar>
<SidebarContent>
<SidebarGroup>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton isActive={isActive}>
{#snippet child({ props })}
<a href="/#/search" {...props}>
<Search class="h-5 w-5" />
<span>Search</span>
</a>
{/snippet}
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroup>
</SidebarContent>
<SidebarFooter>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton onclick={toggleTheme}>
<span>Toggle Theme</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarFooter>
</Sidebar>
SidebarTrigger
Add to pages to show/hide sidebar:
import { SidebarTrigger } from '$lib/components/ui/sidebar';
<div class="mb-4">
<SidebarTrigger />
</div>
Skeleton (Loading States)
From user-integration.svelte:
import { Skeleton } from '$lib/components/ui/skeleton';
{#if isLoading}
<div class="space-y-4">
{#each Array(3) as _}
<div class="flex items-center gap-4">
<Skeleton class="h-12 w-12 rounded" />
<div class="space-y-2">
<Skeleton class="h-5 w-32" />
<Skeleton class="h-4 w-64" />
</div>
</div>
{/each}
</div>
{/if}
AlertDialog (Confirmation)
From user-integration.svelte:
import * as AlertDialog from '$lib/components/ui/alert-dialog';
<AlertDialog.Root open={integrationToDelete !== null}>
<AlertDialog.Content>
<AlertDialog.Header>
<AlertDialog.Title>Remove Integration?</AlertDialog.Title>
<AlertDialog.Description>
Are you sure you want to remove "{integrationToDelete?.name}"?
This action cannot be undone.
</AlertDialog.Description>
</AlertDialog.Header>
<AlertDialog.Footer>
<AlertDialog.Cancel onclick={handleCancel}>
Cancel
</AlertDialog.Cancel>
<AlertDialog.Action onclick={handleConfirm} disabled={isDeleting}>
{isDeleting ? 'Removing...' : 'Remove Integration'}
</AlertDialog.Action>
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog.Root>
Pagination
From user-integration.svelte:
import * as Pagination from '$lib/components/ui/pagination';
<Pagination.Root
count={totalItems}
bind:page={currentPage}
onPageChange={(page) => handlePageChange(page)}
>
{#snippet children({ pages, currentPage })}
<Pagination.Content>
<Pagination.Item>
<Pagination.Previous />
</Pagination.Item>
{#each pages as page (page.key)}
{#if page.type === 'ellipsis'}
<Pagination.Item>
<Pagination.Ellipsis />
</Pagination.Item>
{:else}
<Pagination.Item>
<Pagination.Link {page} isActive={currentPage === page.value}>
{page.value}
</Pagination.Link>
</Pagination.Item>
{/if}
{/each}
<Pagination.Item>
<Pagination.Next />
</Pagination.Item>
</Pagination.Content>
{/snippet}
</Pagination.Root>
Toast Notifications
From user-integration.svelte:
import { toast } from 'svelte-sonner';
import { Toaster } from '$lib/components/ui/sonner';
// Add to page
<Toaster />
// Success toast
toast.success('Success', {
description: 'Integration removed successfully',
});
// Error toast
toast.error('Error', {
description: 'Failed to remove integration. Please try again.',
});
// Info toast
toast('Info', {
description: 'Processing your request...',
});
Icons
From lucide-svelte:
import { Search, Eye, Trash, Settings, User } from 'lucide-svelte';
<Search class="h-5 w-5" />
<Eye class="h-4 w-4" />
Common sizes: h-4 w-4 (16px), h-5 w-5 (20px), h-6 w-6 (24px)
Browse all icons: Lucide Icons
Dark Mode Integration
All components automatically support dark mode via Tailwind's dark: prefix. The theme is managed by $lib/stores/theme.ts.
Missing Components
Some Shadcn components are not installed. Workarounds:
Badge (not available)
Use custom Tailwind classes:
// Status badge
<span class="rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-medium text-green-800 dark:bg-green-900 dark:text-green-200">
Active
</span>
<span class="rounded-full bg-red-100 px-2.5 py-0.5 text-xs font-medium text-red-800 dark:bg-red-900 dark:text-red-200">
Error
</span>
Best Practices
- Import components from
$lib/components/ui/ - Use Tailwind classes for styling (e.g.,
class="gap-2") - Follow dark mode patterns with
dark:prefix - Use Skeleton components for loading states
- Use AlertDialog for destructive actions
- Use toast notifications for user feedback
- Check component exists before using (not all Shadcn components installed)
Adding New Components
To add a new Shadcn component:
npx shadcn-svelte@latest add [component-name]
Example:
npx shadcn-svelte@latest add card
This copies the component into src/lib/components/ui/.