Claude
Skills
Sign in
Back

sveltekit

Included with Lifetime
$97 forever

SvelteKit - Full-stack Svelte framework with file-based routing, SSR/SSG, form actions, and adapters for deployment

toolchainsveltekitsveltessrssgroutingformsfullstackframework

What this skill does


# SvelteKit - Full-Stack Svelte Framework

## Overview

SvelteKit is the official **full-stack framework** for Svelte, providing file-based routing, server-side rendering (SSR), static site generation (SSG), form handling with progressive enhancement, and deployment adapters for any platform.

**Key Features**:
- **File-based routing**: Automatic routes from `src/routes/` directory structure
- **Load functions**: Type-safe data fetching (`+page.ts`, `+page.server.ts`)
- **Form actions**: Native form handling with progressive enhancement
- **SSR/SSG/SPA**: Flexible rendering modes with per-route control
- **Adapters**: Deploy to Vercel, Netlify, Node.js, Cloudflare, and more
- **TypeScript-first**: Generated types from `$types` for type safety
- **Hooks**: Middleware-like `handle`, `handleError`, `handleFetch`
- **API routes**: `+server.ts` files for REST endpoints

**Installation**:
```bash
# Create new SvelteKit project
npm create svelte@latest my-app
cd my-app
npm install
npm run dev -- --open

# Templates: skeleton, demo app, library
# Choices: TypeScript, ESLint, Prettier, Playwright, Vitest
```

## Project Structure

### Standard SvelteKit Layout

```
my-sveltekit-app/
├── src/
│   ├── routes/                   # File-based routing
│   │   ├── +page.svelte         # / (home page)
│   │   ├── +page.ts             # Universal load function
│   │   ├── +page.server.ts      # Server-only load function
│   │   ├── +layout.svelte       # Shared layout
│   │   ├── +layout.ts           # Layout load function
│   │   ├── +error.svelte        # Error page
│   │   ├── about/
│   │   │   └── +page.svelte     # /about
│   │   ├── blog/
│   │   │   ├── +page.svelte     # /blog (list)
│   │   │   ├── +page.server.ts  # Load posts
│   │   │   └── [slug]/
│   │   │       ├── +page.svelte # /blog/my-post
│   │   │       └── +page.server.ts
│   │   └── api/
│   │       └── posts/
│   │           └── +server.ts   # GET /api/posts
│   ├── lib/
│   │   ├── components/
│   │   ├── server/              # Server-only utilities
│   │   │   └── database.ts
│   │   ├── stores/
│   │   └── utils/
│   ├── hooks.server.ts          # Server hooks
│   ├── hooks.client.ts          # Client hooks
│   ├── app.html                 # HTML template
│   └── app.d.ts                 # TypeScript declarations
├── static/                       # Static assets (robots.txt, favicon)
├── tests/                        # Playwright tests
├── svelte.config.js             # SvelteKit configuration
├── vite.config.ts               # Vite configuration
└── package.json
```

## File-Based Routing

### Route Conventions

**File naming determines routing**:

| File | Route | Purpose |
|------|-------|---------|
| `+page.svelte` | `/` | Page component |
| `+page.ts` | - | Universal load (client + server) |
| `+page.server.ts` | - | Server-only load |
| `+layout.svelte` | - | Shared layout |
| `+layout.ts` | - | Layout load |
| `+layout.server.ts` | - | Server layout load |
| `+server.ts` | `/api/...` | API endpoint (GET/POST/etc) |
| `+error.svelte` | - | Error boundary |

### Basic Routes

```
src/routes/
├── +page.svelte              # / (home)
├── about/
│   └── +page.svelte          # /about
├── contact/
│   └── +page.svelte          # /contact
└── pricing/
    └── +page.svelte          # /pricing
```

### Dynamic Routes

```
src/routes/
└── blog/
    ├── +page.svelte          # /blog (list)
    ├── [slug]/
    │   └── +page.svelte      # /blog/my-post
    └── [category]/
        └── [slug]/
            └── +page.svelte  # /blog/tech/my-post
```

**Access route params**:
```svelte
<!-- src/routes/blog/[slug]/+page.svelte -->
<script lang="ts">
  import type { PageData } from './$types';

  let { data } = $props<{ data: PageData }>();
</script>

<article>
  <h1>{data.post.title}</h1>
  <p>{data.post.content}</p>
</article>
```

### Optional Parameters

```
src/routes/
└── archive/
    └── [[year]]/
        └── [[month]]/
            └── +page.svelte  # /archive, /archive/2024, /archive/2024/11
```

```typescript
// src/routes/archive/[[year]]/[[month]]/+page.ts
import type { PageLoad } from './$types';

export const load: PageLoad = async ({ params }) => {
  const year = params.year || new Date().getFullYear();
  const month = params.month || null;

  return {
    year,
    month,
    posts: await fetchPosts({ year, month })
  };
};
```

### Rest Parameters

```
src/routes/
└── docs/
    └── [...path]/
        └── +page.svelte      # /docs/guide/intro, /docs/api/reference
```

```typescript
// src/routes/docs/[...path]/+page.ts
export const load: PageLoad = async ({ params }) => {
  const path = params.path; // "guide/intro"
  const segments = path.split('/'); // ["guide", "intro"]

  return {
    doc: await fetchDoc(path)
  };
};
```

## Load Functions

### Universal Load (+page.ts)

Runs on both server and client. Must use `fetch` for data fetching.

```typescript
// src/routes/products/+page.ts
import type { PageLoad } from './$types';

export const load: PageLoad = async ({ fetch, params, url }) => {
  const response = await fetch('/api/products');
  const products = await response.json();

  return {
    products,
    searchQuery: url.searchParams.get('q') || ''
  };
};

// Prerendering options
export const prerender = true; // Static generation
export const ssr = false;      // Disable SSR (SPA mode)
export const csr = true;       // Enable client-side rendering
```

### Server-Only Load (+page.server.ts)

Runs only on server. Direct database access allowed.

```typescript
// src/routes/dashboard/+page.server.ts
import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
import { db } from '$lib/server/database';

export const load: PageServerLoad = async ({ locals, cookies }) => {
  // Check authentication
  if (!locals.user) {
    throw redirect(303, '/login');
  }

  // Direct database query (server-only)
  const stats = await db.query.stats.findFirst({
    where: eq(stats.userId, locals.user.id)
  });

  // Sensitive data stays on server
  const apiKey = process.env.SECRET_API_KEY;
  const data = await fetchPrivateData(apiKey);

  return {
    stats,
    userData: data
  };
};
```

### Streaming with Promises

```typescript
// src/routes/posts/+page.server.ts
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async () => {
  return {
    // Immediate data
    featured: await db.posts.findMany({ where: { featured: true } }),

    // Streamed data (loads async)
    recent: db.posts.findMany({ orderBy: { createdAt: 'desc' } }),
    popular: db.posts.findMany({ orderBy: { views: 'desc' } })
  };
};
```

```svelte
<!-- src/routes/posts/+page.svelte -->
<script lang="ts">
  import type { PageData } from './$types';

  let { data } = $props<{ data: PageData }>();
</script>

<h2>Featured</h2>
{#each data.featured as post}
  <article>{post.title}</article>
{/each}

<h2>Recent</h2>
{#await data.recent}
  <p>Loading recent posts...</p>
{:then posts}
  {#each posts as post}
    <article>{post.title}</article>
  {/each}
{/await}

<h2>Popular</h2>
{#await data.popular}
  <p>Loading popular posts...</p>
{:then posts}
  {#each posts as post}
    <article>{post.title}</article>
  {/each}
{/await}
```

## Layouts

### Shared Layout

```svelte
<!-- src/routes/+layout.svelte -->
<script lang="ts">
  import Header from '$lib/components/Header.svelte';
  import Footer from '$lib/components/Footer.svelte';
  import type { LayoutData } from './$types';

  let { data, children } = $props<{ data: LayoutData, children: any }>();
</script>

<div class="app">
  <Header user={data.user} />

  <main>
    {@render children()}
  </main>

  <Footer />
</div>

<style>
  .app {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
  }

  main {
    flex: 1;
  }
</style>
```

### Layout Load

```typescript
// src/routes/+layout.server.ts
import type { LayoutServerLoad } from './$types';

export const load: LayoutServerLoad = async ({ locals }

Related in toolchain