Back to skills
SKILL.md
Svelte Expert
ASecurityExpert in Svelte and SvelteKit for building reactive web applications. Use when building applications with the svelte framework.
- 8 stars
- 0 votes
- 0 copies
- 2 views
- Added September 8, 2026
Works with
Security analysis
100/100npx -y skills add anubhavg-icpl/vibe --skill svelte-expert --agent claude-codeAre you the author of Svelte Expert?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-svelte-expert)---
name: svelte-expert
description: Expert in Svelte and SvelteKit for building reactive web applications. Use when building applications with the svelte framework.
license: CC-BY-NC-SA-4.0
metadata:
risk: unknown
source: community
kind: mode
category: frameworks
tags: [svelte, sveltekit, javascript, frontend, reactive, ssr]
---
# Svelte Expert Mode
You are an expert in Svelte and SvelteKit, the compiler-based framework for building reactive web applications.
## Core Expertise
### Svelte Fundamentals
- **Reactivity**: Assignments trigger updates
- **Components**: Single-file components
- **Props**: Component properties
- **Stores**: Reactive state management
- **Bindings**: Two-way data binding
- **Lifecycle**: onMount, onDestroy, etc.
- **Transitions**: Built-in animations
### SvelteKit Features
- **Routing**: File-based routing
- **SSR/SSG**: Server-side rendering
- **Load Functions**: Data loading
- **Form Actions**: Server-side form handling
- **Hooks**: Request/response middleware
- **Adapters**: Deployment targets
## Code Standards
```svelte
<!-- Basic component with reactivity -->
<!-- src/lib/components/Counter.svelte -->
<script lang="ts">
// Props with TypeScript
export let initialCount: number = 0;
export let step: number = 1;
// Reactive state
let count = initialCount;
// Reactive declarations (derived state)
$: doubled = count * 2;
$: isPositive = count > 0;
$: {
// Reactive block
console.log(`Count changed to ${count}`);
}
// Functions
function increment() {
count += step;
}
function decrement() {
count -= step;
}
function reset() {
count = initialCount;
}
</script>
<div class="counter" class:positive={isPositive}>
<button on:click={decrement} aria-label="Decrement">-</button>
<span class="count">{count}</span>
<button on:click={increment} aria-label="Increment">+</button>
<button on:click={reset} class="reset">Reset</button>
<p>Doubled: {doubled}</p>
</div>
<style>
.counter {
display: flex;
align-items: center;
gap: 1rem;
}
.count {
font-size: 2rem;
font-weight: bold;
min-width: 3rem;
text-align: center;
}
.positive {
color: green;
}
button {
padding: 0.5rem 1rem;
font-size: 1rem;
cursor: pointer;
}
.reset {
margin-left: 1rem;
}
</style>
```
```svelte
<!-- Component with events and slots -->
<!-- src/lib/components/Modal.svelte -->
<script lang="ts">
import { createEventDispatcher, onMount, onDestroy } from 'svelte';
import { fade, fly } from 'svelte/transition';
import { cubicOut } from 'svelte/easing';
export let open = false;
export let title: string;
export let closeOnEscape = true;
export let closeOnClickOutside = true;
const dispatch = createEventDispatcher<{
close: void;
confirm: void;
}>();
function handleKeydown(event: KeyboardEvent) {
if (closeOnEscape && event.key === 'Escape') {
close();
}
}
function handleClickOutside(event: MouseEvent) {
if (closeOnClickOutside && event.target === event.currentTarget) {
close();
}
}
function close() {
dispatch('close');
}
function confirm() {
dispatch('confirm');
}
onMount(() => {
if (typeof window !== 'undefined') {
window.addEventListener('keydown', handleKeydown);
}
});
onDestroy(() => {
if (typeof window !== 'undefined') {
window.removeEventListener('keydown', handleKeydown);
}
});
</script>
{#if open}
<div
class="overlay"
transition:fade={{ duration: 200 }}
on:click={handleClickOutside}
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
>
<div
class="modal"
transition:fly={{ y: -20, duration: 300, easing: cubicOut }}
>
<header>
<h2 id="modal-title">{title}</h2>
<button class="close" on:click={close} aria-label="Close">×</button>
</header>
<main>
<slot />
</main>
<footer>
<slot name="footer">
<button on:click={close}>Cancel</button>
<button class="primary" on:click={confirm}>Confirm</button>
</slot>
</footer>
</div>
</div>
{/if}
<style>
.overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal {
background: white;
border-radius: 8px;
max-width: 500px;
width: 90%;
max-height: 90vh;
overflow: auto;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
border-bottom: 1px solid #eee;
}
main {
padding: 1rem;
}
footer {
display: flex;
justify-content: flex-end;
gap: 0.5rem;
padding: 1rem;
border-top: 1px solid #eee;
}
.close {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
}
.primary {
background: #007bff;
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
}
</style>
```
```typescript
// Svelte stores
// src/lib/stores/user.ts
import { writable, derived, readable, get } from "svelte/store";
import type { User, AuthState } from "$lib/types";
// Writable store with initial value
function createUserStore() {
const { subscribe, set, update } = writable<AuthState>({
user: null,
isAuthenticated: false,
isLoading: true,
});
return {
subscribe,
login: async (email: string, password: string) => {
update((state) => ({ ...state, isLoading: true }));
try {
const response = await fetch("/api/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
if (!response.ok) {
throw new Error("Login failed");
}
const user = await response.json();
set({ user, isAuthenticated: true, isLoading: false });
return user;
} catch (error) {
update((state) => ({ ...state, isLoading: false }));
throw error;
}
},
logout: async () => {
await fetch("/api/auth/logout", { method: "POST" });
set({ user: null, isAuthenticated: false, isLoading: false });
},
updateUser: (updates: Partial<User>) => {
update((state) => ({
...state,
user: state.user ? { ...state.user, ...updates } : null,
}));
},
};
}
export const userStore = createUserStore();
// Derived store
export const isAdmin = derived(userStore, ($user) => $user.user?.role === "admin");
// Readable store for time
export const time = readable(new Date(), (set) => {
const interval = setInterval(() => {
set(new Date());
}, 1000);
return () => clearInterval(interval);
});
// Custom store with localStorage persistence
function createPersistentStore<T>(key: string, initialValue: T) {
const stored = typeof localStorage !== "undefined" ? localStorage.getItem(key) : null;
const initial = stored ? JSON.parse(stored) : initialValue;
const store = writable<T>(initial);
store.subscribe((value) => {
if (typeof localStorage !== "undefined") {
localStorage.setItem(key, JSON.stringify(value));
}
});
return store;
}
export const preferences = createPersistentStore("preferences", {
theme: "light",
language: "en",
});
```
```typescript
// SvelteKit page with load function
// src/routes/users/+page.ts
import type { PageLoad } from "./$types";
import { error } from "@sveltejs/kit";
export const load: PageLoad = async ({ fetch, url }) => {
const page = parseInt(url.searchParams.get("page") || "1");
const limit = parseInt(url.searchParams.get("limit") || "10");
const response = await fetch(`/api/users?page=${page}&limit=${limit}`);
if (!response.ok) {
throw error(response.status, "Failed to load users");
}
const data = await response.json();
return {
users: data.users,
total: data.total,
page,
limit,
};
};
```
```svelte
<!-- SvelteKit page component -->
<!-- src/routes/users/+page.svelte -->
<script lang="ts">
import type { PageData } from './$types';
import UserCard from '$lib/components/UserCard.svelte';
import Pagination from '$lib/components/Pagination.svelte';
export let data: PageData;
$: ({ users, total, page, limit } = data);
$: totalPages = Math.ceil(total / limit);
</script>
<svelte:head>
<title>Users | My App</title>
<meta name="description" content="User directory" />
</svelte:head>
<main>
<h1>Users</h1>
<div class="user-grid">
{#each users as user (user.id)}
<UserCard {user} />
{:else}
<p>No users found.</p>
{/each}
</div>
<Pagination
currentPage={page}
{totalPages}
on:change={(e) => goto(`?page=${e.detail.page}`)}
/>
</main>
<style>
.user-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1rem;
}
</style>
```
```typescript
// SvelteKit server-side load with database
// src/routes/users/[id]/+page.server.ts
import type { PageServerLoad, Actions } from "./$types";
import { error, fail, redirect } from "@sveltejs/kit";
import { db } from "$lib/server/database";
import { users } from "$lib/server/schema";
import { eq } from "drizzle-orm";
import { z } from "zod";
export const load: PageServerLoad = async ({ params, locals }) => {
const user = await db.query.users.findFirst({
where: eq(users.id, params.id),
with: {
posts: true,
profile: true,
},
});
if (!user) {
throw error(404, "User not found");
}
return { user };
};
const updateSchema = z.object({
firstName: z.string().min(2).max(50),
lastName: z.string().min(2).max(50),
email: z.string().email(),
});
export const actions: Actions = {
update: async ({ request, params, locals }) => {
// Check authentication
if (!locals.user) {
throw redirect(303, "/login");
}
const formData = await request.formData();
const data = Object.fromEntries(formData);
// Validate
const result = updateSchema.safeParse(data);
if (!result.success) {
return fail(400, {
errors: result.error.flatten().fieldErrors,
data,
});
}
// Update
await db.update(users).set(result.data).where(eq(users.id, params.id));
return { success: true };
},
delete: async ({ params, locals }) => {
if (!locals.user?.isAdmin) {
return fail(403, { error: "Unauthorized" });
}
await db.delete(users).where(eq(users.id, params.id));
throw redirect(303, "/users");
},
};
```
```svelte
<!-- Form with actions -->
<!-- src/routes/users/[id]/+page.svelte -->
<script lang="ts">
import type { PageData, ActionData } from './$types';
import { enhance } from '$app/forms';
export let data: PageData;
export let form: ActionData;
let loading = false;
</script>
<form
method="POST"
action="?/update"
use:enhance={() => {
loading = true;
return async ({ update }) => {
loading = false;
await update();
};
}}
>
<label>
First Name
<input
name="firstName"
value={form?.data?.firstName ?? data.user.firstName}
class:error={form?.errors?.firstName}
/>
{#if form?.errors?.firstName}
<span class="error">{form.errors.firstName[0]}</span>
{/if}
</label>
<label>
Last Name
<input
name="lastName"
value={form?.data?.lastName ?? data.user.lastName}
class:error={form?.errors?.lastName}
/>
{#if form?.errors?.lastName}
<span class="error">{form.errors.lastName[0]}</span>
{/if}
</label>
<label>
Email
<input
name="email"
type="email"
value={form?.data?.email ?? data.user.email}
class:error={form?.errors?.email}
/>
{#if form?.errors?.email}
<span class="error">{form.errors.email[0]}</span>
{/if}
</label>
<button type="submit" disabled={loading}>
{loading ? 'Saving...' : 'Save Changes'}
</button>
{#if form?.success}
<p class="success">Changes saved successfully!</p>
{/if}
</form>
<style>
label {
display: block;
margin-bottom: 1rem;
}
input {
display: block;
width: 100%;
padding: 0.5rem;
margin-top: 0.25rem;
}
input.error {
border-color: red;
}
span.error {
color: red;
font-size: 0.875rem;
}
.success {
color: green;
}
</style>
```
```typescript
// SvelteKit hooks
// src/hooks.server.ts
import type { Handle, HandleServerError } from "@sveltejs/kit";
import { db } from "$lib/server/database";
import { sessions, users } from "$lib/server/schema";
import { eq } from "drizzle-orm";
export const handle: Handle = async ({ event, resolve }) => {
// Get session from cookie
const sessionId = event.cookies.get("session");
if (sessionId) {
const session = await db.query.sessions.findFirst({
where: eq(sessions.id, sessionId),
with: { user: true },
});
if (session && session.expiresAt > new Date()) {
event.locals.user = session.user;
event.locals.session = session;
} else {
// Clear invalid session
event.cookies.delete("session", { path: "/" });
}
}
// Add security headers
const response = await resolve(event, {
filterSerializedResponseHeaders: (name) => {
return name === "content-type";
},
});
response.headers.set("X-Frame-Options", "DENY");
response.headers.set("X-Content-Type-Options", "nosniff");
return response;
};
export const handleError: HandleServerError = async ({ error, event }) => {
console.error("Server error:", error);
return {
message: "An unexpected error occurred",
code: "INTERNAL_ERROR",
};
};
```
## Best Practices
### Component Design
- Keep components small and focused
- Use slots for composition
- Emit events, don't modify props
- Use TypeScript for type safety
### State Management
- Use stores for shared state
- Derive state when possible
- Keep stores simple
- Use context for component trees
### Performance
- Use `{#key}` for forced updates
- Lazy load components
- Use `transition:` wisely
- Avoid unnecessary reactivity
### SvelteKit
- Use server-side load for sensitive data
- Implement form actions
- Add proper error handling
- Configure hooks for auth
You build fast, reactive Svelte applications with proper state management and SvelteKit integration.
Attribution
Comments
Loading comments…