teta.so

SvelteKit Tutorial: Build Your First App in 2026

A beginner-friendly SvelteKit tutorial that walks you through building your first app with routing, data loading, forms, and deployment.

SvelteKit is a full-stack web framework that makes building fast, modern web applications straightforward. If you have been curious about Svelte but have not taken the plunge, this tutorial will walk you through building a complete application from scratch — project setup, routing, data loading, form handling, and deployment to Vercel. By the end, you will have a working app and a solid understanding of how SvelteKit works. If you prefer building with AI assistance, Teta lets you create SvelteKit apps by describing what you want in chat, but understanding the fundamentals covered here will make you effective with any tool.

What Is SvelteKit

SvelteKit is the official application framework built on top of Svelte. While Svelte is a component framework (think of it as an alternative to React or Vue), SvelteKit is the full-stack layer that adds everything you need to build a real application: file-based routing, server-side rendering, API endpoints, form actions, and a build system.

The key distinction between Svelte and other component frameworks is that Svelte is a compiler. When you build your project, Svelte compiles your components into efficient vanilla JavaScript that directly manipulates the DOM. There is no virtual DOM, no runtime framework library shipped to the browser. This means smaller bundles, faster page loads, and better performance on low-powered devices.

SvelteKit builds on this foundation with:

  • File-based routing — your file structure defines your URL structure
  • Server-side rendering (SSR) — pages render on the server for fast initial loads and SEO
  • Load functions — fetch data on the server before rendering a page
  • Form actions — handle form submissions on the server with progressive enhancement
  • Adapters — deploy to Vercel, Netlify, Cloudflare, Node.js, or static hosting
  • Vite — lightning-fast development with hot module replacement

As of 2026, Svelte 5 is the default version, introducing runes as the reactivity system. Runes replace the older $: reactive declarations with explicit functions like $state(), $derived(), and $effect(), making reactivity more predictable and TypeScript-friendly.

Setting Up Your SvelteKit Project

Let's build a simple task manager application. Start by creating a new SvelteKit project.

Open your terminal and run:

npx sv create task-manager

The CLI will ask you a few questions. Select these options:

  • Template: SvelteKit minimal
  • Type checking: TypeScript
  • Additional options: Choose any extras you want (ESLint, Prettier, etc.)

Once the scaffolding completes, install dependencies and start the development server:

cd task-manager
npm install
npm run dev

Open http://localhost:5173 in your browser. You should see the default SvelteKit welcome page. The dev server uses Vite, so changes you make to files will appear instantly in the browser without a full page reload.

Project Structure Explained

Let's look at the directory structure SvelteKit creates:

task-manager/
  src/
    routes/
      +page.svelte        ← Home page component
      +layout.svelte      ← Root layout (wraps all pages)
    app.html              ← HTML shell
    app.d.ts              ← TypeScript declarations
  static/                 ← Static assets (images, fonts)
  svelte.config.js        ← SvelteKit configuration
  vite.config.ts          ← Vite configuration
  tsconfig.json           ← TypeScript configuration
  package.json

The most important directory is src/routes/. Every file and folder inside it defines a route in your application. Here is how the mapping works:

File path URL
src/routes/+page.svelte /
src/routes/about/+page.svelte /about
src/routes/tasks/+page.svelte /tasks
src/routes/tasks/[id]/+page.svelte /tasks/123 (dynamic)

The + prefix is intentional. It tells SvelteKit "this is a route file, not a regular component." Files without the + prefix in the routes directory are treated as private modules that won't be accessible as pages.

+layout.svelte is a special file that wraps all pages at the same level and below. It is where you put your navigation, footer, and any shared UI elements.

+page.svelte is the page component. It receives data from the corresponding load function and renders the page content.

Routing and Pages

Let's create the pages for our task manager. First, update the root layout to include navigation.

Replace the contents of src/routes/+layout.svelte:

<script>
  let { children } = $props();
</script>

<nav>
  <a href="/">Home</a>
  <a href="/tasks">Tasks</a>
  <a href="/about">About</a>
</nav>

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

<style>
  nav {
    display: flex;
    gap: 16px;
    padding: 16px 24px;
    border-bottom: 1px solid #e5e5e5;
  }

  nav a {
    text-decoration: none;
    color: #333;
    font-weight: 500;
  }

  main {
    max-width: 800px;
    margin: 0 auto;
    padding: 24px;
  }
</style>

Now create the home page. Replace src/routes/+page.svelte:

<h1>Task Manager</h1>
<p>A simple task manager built with SvelteKit.</p>
<a href="/tasks">View Tasks →</a>

Create the tasks page at src/routes/tasks/+page.svelte:

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

<h1>Tasks</h1>

{#if data.tasks.length === 0}
  <p>No tasks yet. Add one below.</p>
{:else}
  <ul>
    {#each data.tasks as task}
      <li class:done={task.done}>
        <span>{task.title}</span>
      </li>
    {/each}
  </ul>
{/if}

Create the about page at src/routes/about/+page.svelte:

<h1>About</h1>
<p>This is a task manager built with SvelteKit as a learning exercise.</p>

SvelteKit handles client-side navigation automatically. When you click a link between pages, the framework fetches only the data and components needed for the new page — no full page reload. This gives your app the snappy feel of a single-page application while keeping the SEO and accessibility benefits of server-rendered pages.

Loading Data with Server Load Functions

The tasks page needs data. SvelteKit uses load functions to fetch data on the server before a page renders. Create src/routes/tasks/+page.server.ts:

// src/routes/tasks/+page.server.ts

// In a real app, this would come from a database
let tasks = [
  { id: '1', title: 'Learn SvelteKit routing', done: true },
  { id: '2', title: 'Build a load function', done: false },
  { id: '3', title: 'Deploy to Vercel', done: false }
];

export function load() {
  return {
    tasks: tasks.map(t => ({ ...t }))
  };
}

The load function runs on the server before the page renders. Whatever you return becomes available in the page component through the data prop. SvelteKit automatically types this connection — your editor knows that data.tasks exists and what shape it has.

A few important details about load functions:

  • They run on every request (for server load functions). This means your data is always fresh.
  • They can access server-only resources like databases, environment variables, and file systems.
  • They run before the page HTML is generated, so the page is fully rendered when it arrives at the browser — great for SEO and initial load performance.
  • The load function receives a context object with params (URL parameters), url (the full URL), cookies, request, and more.

For dynamic routes, you access URL parameters through the context:

// src/routes/tasks/[id]/+page.server.ts
export function load({ params }) {
  const task = tasks.find(t => t.id === params.id);
  if (!task) {
    throw error(404, 'Task not found');
  }
  return { task };
}

Forms and Actions

SvelteKit's form actions let you handle form submissions on the server with built-in progressive enhancement. Let's add the ability to create and toggle tasks.

Update src/routes/tasks/+page.server.ts to include actions:

// src/routes/tasks/+page.server.ts
import { fail } from '@sveltejs/kit';

let tasks = [
  { id: '1', title: 'Learn SvelteKit routing', done: true },
  { id: '2', title: 'Build a load function', done: false },
  { id: '3', title: 'Deploy to Vercel', done: false }
];

export function load() {
  return {
    tasks: tasks.map(t => ({ ...t }))
  };
}

export const actions = {
  create: async ({ request }) => {
    const formData = await request.formData();
    const title = formData.get('title')?.toString().trim();

    if (!title) {
      return fail(400, { error: 'Title is required', title });
    }

    tasks.push({
      id: crypto.randomUUID(),
      title,
      done: false
    });

    return { success: true };
  },

  toggle: async ({ request }) => {
    const formData = await request.formData();
    const id = formData.get('id')?.toString();
    const task = tasks.find(t => t.id === id);
    if (task) {
      task.done = !task.done;
    }
  }
};

Now update the tasks page to include forms:

<!-- src/routes/tasks/+page.svelte -->
<script>
  import { enhance } from '$app/forms';
  let { data, form } = $props();
</script>

<h1>Tasks</h1>

<form method="POST" action="?/create" use:enhance>
  <input
    name="title"
    placeholder="Add a new task..."
    value={form?.title ?? ''}
    required
  />
  <button type="submit">Add</button>
</form>

{#if form?.error}
  <p class="error">{form.error}</p>
{/if}

{#if data.tasks.length === 0}
  <p>No tasks yet. Add one above.</p>
{:else}
  <ul>
    {#each data.tasks as task}
      <li class:done={task.done}>
        <form method="POST" action="?/toggle" use:enhance>
          <input type="hidden" name="id" value={task.id} />
          <button type="submit">{task.done ? '✓' : '○'}</button>
        </form>
        <span>{task.title}</span>
      </li>
    {/each}
  </ul>
{/if}

<style>
  form {
    display: flex;
    gap: 8px;
    margin-bottom: 24px;
  }

  input[name="title"] {
    flex: 1;
    padding: 8px 12px;
    border: 1px solid #ddd;
    font-size: 1rem;
  }

  ul {
    list-style: none;
    padding: 0;
  }

  li {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 0;
    border-bottom: 1px solid #f0f0f0;
  }

  li.done span {
    text-decoration: line-through;
    opacity: 0.5;
  }

  .error {
    color: #e53e3e;
    font-size: 0.875rem;
  }
</style>

The use:enhance directive is key. Without it, form submissions cause a full page reload (standard HTML behavior). With it, SvelteKit intercepts the submission, sends it via fetch, and updates the page data without a reload. If JavaScript is disabled, the forms still work — they just cause a full page navigation instead. This is progressive enhancement.

The form prop contains whatever data the action returned (including validation errors from fail()), letting you display error messages and preserve input values.

Deploying to Vercel

SvelteKit makes deployment straightforward with its adapter system. To deploy to Vercel, install the Vercel adapter:

npm install -D @sveltejs/adapter-vercel

Update svelte.config.js:

import adapter from '@sveltejs/adapter-vercel';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  preprocess: vitePreprocess(),
  kit: {
    adapter: adapter()
  }
};

export default config;

Now deploy. If you have the Vercel CLI installed:

npx vercel

Alternatively, push your project to GitHub and connect the repository in the Vercel dashboard. Vercel auto-detects SvelteKit and configures the build settings for you. Every push to your main branch triggers a new deployment.

Your app will be live at a .vercel.app URL within minutes. Custom domains can be configured in the Vercel dashboard.

Next Steps

You now have a working SvelteKit application with routing, server-side data loading, form handling, and deployment. Here are the logical next steps to continue learning:

Add a database. Replace the in-memory task array with a real database. Supabase (PostgreSQL) and Prisma are popular choices in the SvelteKit ecosystem. Your load functions and form actions already run on the server, so connecting a database is straightforward.

Add authentication. Protect your tasks so each user sees only their own. SvelteKit's hooks system (src/hooks.server.ts) lets you check authentication on every request. Supabase Auth, Lucia, and Auth.js all work well with SvelteKit.

Add error handling. SvelteKit has a built-in error page system. Create src/routes/+error.svelte to customize what users see when something goes wrong.

Explore layouts. Nested layouts let you share UI between groups of pages. For example, all pages under /tasks could share a sidebar layout while /about uses a simpler layout.

Use Teta to build faster. Once you understand SvelteKit's fundamentals, tools like Teta let you describe features in plain English and generate production-quality SvelteKit code. Knowing the framework means you can review what the AI produces and guide it effectively — the combination of SvelteKit knowledge and AI assistance is a powerful workflow.

Read the official docs. The SvelteKit documentation is excellent and covers advanced topics like streaming, prerendering, service workers, and custom adapters.

FAQ

What is SvelteKit used for?

SvelteKit is used to build full-stack web applications, from simple static sites to complex dynamic apps. It handles routing, server-side rendering, API endpoints, and form processing in a single framework. Common use cases include SaaS products, marketing websites, dashboards, e-commerce storefronts, blogs, and internal tools.

Is SvelteKit hard to learn?

SvelteKit has one of the gentlest learning curves of any full-stack framework. Svelte's syntax is close to plain HTML, CSS, and JavaScript, so if you know web basics, you can start building immediately. The main concepts — routing, load functions, and form actions — are straightforward and well-documented. Most developers report being productive within a few days.

How do I deploy a SvelteKit app?

SvelteKit uses an adapter system for deployment. Install the adapter for your hosting platform (Vercel, Netlify, Cloudflare, Node.js, or static), update svelte.config.js to use it, and deploy. For Vercel, you can either use the Vercel CLI or connect a GitHub repository for automatic deployments on every push.

Can I use TypeScript with SvelteKit?

Yes, SvelteKit has first-class TypeScript support. When you create a new project, you can select TypeScript during setup. SvelteKit automatically generates types for your load functions, form actions, and page data, giving you end-to-end type safety. Svelte 5's runes system was designed with TypeScript in mind, and type inference works well throughout.

What database should I use with SvelteKit?

The most popular choice in the SvelteKit ecosystem is Supabase, which provides a hosted PostgreSQL database with a JavaScript client library, real-time subscriptions, and built-in authentication. Prisma is another popular option that works with multiple database backends. For simpler projects, SQLite with Drizzle ORM or Turso is lightweight and fast. SvelteKit does not mandate a specific database — any database with a JavaScript/TypeScript client works.

Frequently Asked Questions

What is SvelteKit used for?

SvelteKit is used to build full-stack web applications, from simple static sites to complex dynamic apps. It handles routing, server-side rendering, API endpoints, and form processing in a single framework. Common use cases include SaaS products, marketing websites, dashboards, e-commerce storefronts, blogs, and internal tools.

Is SvelteKit hard to learn?

SvelteKit has one of the gentlest learning curves of any full-stack framework. Svelte's syntax is close to plain HTML, CSS, and JavaScript, so if you know web basics, you can start building immediately. The main concepts — routing, load functions, and form actions — are straightforward and well-documented. Most developers report being productive within a few days.

How do I deploy a SvelteKit app?

SvelteKit uses an adapter system for deployment. Install the adapter for your hosting platform (Vercel, Netlify, Cloudflare, Node.js, or static), update svelte.config.js to use it, and deploy. For Vercel, you can either use the Vercel CLI or connect a GitHub repository for automatic deployments on every push.

Can I use TypeScript with SvelteKit?

Yes, SvelteKit has first-class TypeScript support. When you create a new project, you can select TypeScript during setup. SvelteKit automatically generates types for your load functions, form actions, and page data, giving you end-to-end type safety. Svelte 5's runes system was designed with TypeScript in mind, and type inference works well throughout.

What database should I use with SvelteKit?

The most popular choice in the SvelteKit ecosystem is Supabase, which provides a hosted PostgreSQL database with a JavaScript client library, real-time subscriptions, and built-in authentication. Prisma is another popular option that works with multiple database backends. For simpler projects, SQLite with Drizzle ORM or Turso is lightweight and fast. SvelteKit does not mandate a specific database — any database with a JavaScript/TypeScript client works.

Ready to start building?

Create your next web app with AI-powered development tools.

Kostenlos Starten
← All articles