Skip to content
Back to skills

Svelte Expert

ASecurity

Expert 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
developmentjavascripttypescriptgojavareactapidatabasefrontendsecurityperformance

Works with

  • cursor
  • cli
  • api

Security analysis

A100/100

Scanned September 8, 2026

npx -y skills add anubhavg-icpl/vibe --skill svelte-expert --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Svelte Expert?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Svelte Expert
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/anubhavg-icpl-svelte-expert/badge)](https://www.skillsdirectory.com/skills/anubhavg-icpl-svelte-expert)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…