Frontend Development Skill
Comprehensive skill for modern frontend development with Svelte, SvelteKit, shadcn-svelte, and the Bun ecosystem.
Technology Stack
Core Framework: Svelte + SvelteKit
Svelte Fundamentals
-
Reactive Framework: Compile-time framework that converts components into highly efficient imperative code
-
True Reactivity: Automatic dependency tracking without virtual DOM
-
Small Bundle Size: Minimal runtime overhead
-
Built-in Animations: Transition and animation directives
-
Scoped Styles: Component styles are scoped by default
SvelteKit Features
-
Full-Stack Framework: Server-side rendering (SSR), static site generation (SSG), and client-side rendering (CSR)
-
File-Based Routing: Routes defined by file system structure
-
API Routes: Build API endpoints alongside frontend code
-
Form Actions: Server-side form handling with progressive enhancement
-
Hooks: Intercept and modify requests/responses
-
Adapters: Deploy to any platform (Node, Vercel, Netlify, Cloudflare, etc.)
UI Component Library: shadcn-svelte
Overview
-
Port of shadcn/ui for Svelte
-
Accessible, customizable components built on Radix Svelte (Melt UI)
-
Copy-paste component approach (not an npm package)
-
Built with Tailwind CSS
-
TypeScript support
Key Components
-
Forms: Input, Textarea, Select, Checkbox, Radio, Switch
-
Feedback: Alert, Toast (Sonner), Dialog, Alert Dialog
-
Navigation: Button, Dropdown Menu, Tabs, Command Menu
-
Layout: Card, Separator, Accordion, Collapsible
-
Data Display: Table, Badge, Avatar, Skeleton
-
Overlays: Popover, Tooltip, Sheet, Drawer
Installation & Usage
Initialize shadcn-svelte
bunx shadcn-svelte@latest init
Add components as needed
bunx shadcn-svelte@latest add button bunx shadcn-svelte@latest add card bunx shadcn-svelte@latest add form
Package Manager: Bun
Why Bun?
-
Performance: 25x faster than npm, 4x faster than pnpm
-
All-in-One: Runtime, bundler, test runner, package manager
-
Drop-in Replacement: Compatible with npm/Node.js ecosystem
-
Built-in Testing: Vitest-compatible test runner
-
TypeScript Native: Native TypeScript support, no compilation needed
npm Compatibility
-
Reads package.json and package-lock.json/bun.lockb
-
Works with most npm packages
-
Can run npm scripts via bun run
-
Falls back to npm when needed for specific packages
Project Architecture
Recommended Directory Structure
project-root/ ├── src/ │ ├── lib/ │ │ ├── components/ │ │ │ ├── ui/ # shadcn-svelte components │ │ │ │ ├── button/ │ │ │ │ ├── card/ │ │ │ │ └── ... │ │ │ ├── layout/ # Layout components │ │ │ │ ├── Header.svelte │ │ │ │ ├── Footer.svelte │ │ │ │ └── Sidebar.svelte │ │ │ └── features/ # Feature-specific components │ │ │ ├── auth/ │ │ │ ├── dashboard/ │ │ │ └── ... │ │ ├── stores/ # Svelte stores │ │ │ ├── user.ts │ │ │ ├── theme.ts │ │ │ └── notifications.ts │ │ ├── utils/ # Utility functions │ │ │ ├── api.ts │ │ │ ├── validation.ts │ │ │ └── formatting.ts │ │ ├── types/ # TypeScript types │ │ │ ├── api.ts │ │ │ └── models.ts │ │ ├── server/ # Server-side utilities │ │ │ ├── db.ts │ │ │ └── auth.ts │ │ └── config/ # Configuration │ │ ├── constants.ts │ │ └── env.ts │ ├── routes/ # SvelteKit routes │ │ ├── +page.svelte # Home page │ │ ├── +layout.svelte # Root layout │ │ ├── +error.svelte # Error page │ │ ├── api/ # API routes │ │ │ └── users/ │ │ │ └── +server.ts │ │ ├── (auth)/ # Route group │ │ │ ├── login/ │ │ │ └── register/ │ │ └── dashboard/ │ │ ├── +page.svelte │ │ └── +page.server.ts │ ├── app.html # HTML template │ ├── app.css # Global styles │ └── hooks.server.ts # Server hooks ├── static/ # Static assets │ ├── images/ │ └── fonts/ ├── tests/ # Tests │ ├── unit/ │ └── integration/ ├── bun.lockb # Bun lockfile ├── package.json ├── svelte.config.js ├── vite.config.ts ├── tailwind.config.js └── tsconfig.json
Core Patterns & Best Practices
- Component Development
Component Structure Best Practices
<script lang="ts"> // 1. Imports (external, then internal) import { onMount, createEventDispatcher } from 'svelte'; import { fade } from 'svelte/transition'; import type { User } from '$lib/types'; import { Button } from '$lib/components/ui/button';
// 2. Type definitions (if not in separate file) interface $$Props { user: User; variant?: 'default' | 'compact'; }
// 3. Props with defaults export let user: User; export let variant: $$Props['variant'] = 'default';
// 4. Event dispatcher const dispatch = createEventDispatcher<{ edit: { userId: string }; delete: { userId: string }; }>();
// 5. Local state let isEditing = false; let formData = { ...user };
// 6. Reactive declarations
$: fullName = ${user.firstName} ${user.lastName};
$: hasChanges = JSON.stringify(formData) !== JSON.stringify(user);
// 7. Functions function handleEdit() { isEditing = true; }
function handleSave() { dispatch('edit', { userId: user.id }); isEditing = false; }
// 8. Lifecycle hooks onMount(() => { console.log('Component mounted');
return () => {
console.log('Component will unmount');
};
}); </script>
<!-- Template with clear hierarchy --> <div class="user-card" class:compact={variant === 'compact'}> {#if isEditing} <form on:submit|preventDefault={handleSave}> <!-- Edit mode --> </form> {:else} <!-- View mode --> <div class="user-info" transition:fade> <h3>{fullName}</h3> <p>{user.email}</p> </div> <Button on:click={handleEdit}>Edit</Button> {/if} </div>
<!-- Scoped styles --> <style lang="postcss"> .user-card { @apply rounded-lg border p-4;
&.compact {
@apply p-2;
}
}
.user-info { @apply space-y-2; } </style>
TypeScript Props Pattern
// For complex props, use interface interface $$Props { items: Item[]; selectedId?: string; onSelect?: (item: Item) => void; class?: string; }
export let items: $$Props['items']; export let selectedId: $$Props['selectedId'] = undefined; export let onSelect: $$Props['onSelect'] = undefined;
// For class prop forwarding let className: $$Props['class'] = ''; export { className as class };
- State Management
Svelte Stores
Writable Store
// stores/user.ts import { writable } from 'svelte/store'; import type { User } from '$lib/types';
function createUserStore() { const { subscribe, set, update } = writable<User | null>(null);
return { subscribe, set, login: (user: User) => set(user), logout: () => set(null), updateProfile: (updates: Partial<User>) => update(user => user ? { ...user, ...updates } : null) }; }
export const user = createUserStore();
Derived Store
// stores/user.ts (continued) import { derived } from 'svelte/store';
export const isAuthenticated = derived( user, $user => $user !== null );
export const userPermissions = derived( user, $user => $user?.roles.flatMap(role => role.permissions) ?? [] );
Readable Store (for external data)
import { readable } from 'svelte/store';
export const time = readable(new Date(), (set) => { const interval = setInterval(() => { set(new Date()); }, 1000);
return () => clearInterval(interval); });
Custom Store with Persistence
import { writable } from 'svelte/store'; import { browser } from '$app/environment';
export function persistedStore<T>(key: string, initialValue: T) { const stored = browser ? localStorage.getItem(key) : null; const initial = stored ? JSON.parse(stored) : initialValue;
const store = writable<T>(initial);
if (browser) { store.subscribe(value => { localStorage.setItem(key, JSON.stringify(value)); }); }
return store; }
// Usage export const theme = persistedStore<'light' | 'dark'>('theme', 'light');
Context API (Component Tree State)
<!-- Parent.svelte --> <script lang="ts"> import { setContext } from 'svelte'; import type { Writable } from 'svelte/store'; import { writable } from 'svelte/store';
const formData = writable({ name: '', email: '' }); setContext('form', formData); </script>
<!-- Child.svelte --> <script lang="ts"> import { getContext } from 'svelte'; import type { Writable } from 'svelte/store';
const formData = getContext<Writable<FormData>>('form'); </script>
<input bind:value={$formData.name} />
- SvelteKit Routing & Data Loading
Page Structure
// routes/blog/[slug]/+page.ts import type { PageLoad } from './$types';
export const load: PageLoad = async ({ params, fetch, parent }) => { // Access parent layout data const parentData = await parent();
// Fetch data
const response = await fetch(/api/posts/${params.slug});
const post = await response.json();
return { post, meta: { title: post.title, description: post.excerpt } }; };
<!-- routes/blog/[slug]/+page.svelte --> <script lang="ts"> import type { PageData } from './$types';
export let data: PageData; </script>
<svelte:head> <title>{data.meta.title}</title> <meta name="description" content={data.meta.description} /> </svelte:head>
<article> <h1>{data.post.title}</h1> <div>{@html data.post.content}</div> </article>
Server-Side Data Loading
// routes/dashboard/+page.server.ts import type { PageServerLoad, Actions } from './$types'; import { error, fail, redirect } from '@sveltejs/kit';
export const load: PageServerLoad = async ({ locals, depends }) => { // depends() creates a dependency for invalidation depends('app:dashboard');
if (!locals.user) { throw redirect(307, '/login'); }
try { const stats = await fetchUserStats(locals.user.id); return { stats }; } catch (err) { throw error(500, 'Failed to load dashboard data'); } };
export const actions: Actions = { updateProfile: async ({ request, locals }) => { const formData = await request.formData(); const name = formData.get('name');
if (!name) {
return fail(400, { name, missing: true });
}
await updateUser(locals.user.id, { name });
return { success: true };
} };
Form Actions with Progressive Enhancement
<script lang="ts"> import { enhance } from '$app/forms'; import type { ActionData } from './$types';
export let form: ActionData;
let loading = false; </script>
<form method="POST" action="?/updateProfile" use:enhance={() => { loading = true;
return async ({ result, update }) => {
await update();
loading = false;
if (result.type === 'success') {
// Handle success
}
};
}}
<input name="name" value={form?.name ?? ''} aria-invalid={form?.missing} />
{#if form?.missing} <p class="error">Name is required</p> {/if}
<button disabled={loading}> {loading ? 'Saving...' : 'Save'} </button> </form>
REST API endpoints (GET/POST handlers, pagination) and type-safe API client: see references/api-patterns.md Form handling & validation (shadcn-svelte forms, sveltekit-superforms, Zod): see references/forms-styling.md Tailwind CSS configuration and component styling patterns: see references/forms-styling.md Testing (Vitest, integration), performance (code splitting, lazy loading), accessibility (Melt UI, ARIA): see references/testing-performance-a11y.md Deployment (Bun build, env vars), common patterns (dark mode, toast, auth, SSE), troubleshooting, and resources: see references/deployment-patterns.md