By Vivek - August 13, 2020 · Updated July 16, 2026

Svelte 5 Tutorial - A Thorough Introduction to Svelte

In this SvelteJS guide, we will take a thorough look at what Svelte is, how it differs from virtual-DOM frameworks, and the core concepts you need before building real Svelte and SvelteKit apps. Use this as a starting point, then follow the linked guides for setup, styling, deployment, and browser extension work.

Table of Contents

  1. What is Svelte?
  2. Get Started with Svelte
  3. Svelte Components
  4. Svelte Reactivity
  5. State Management in Svelte
  6. Svelte Snippets
  7. Svelte Bindings
  8. Svelte Lifecycle Hooks
  9. Conditional Logic in Svelte Templates
  10. Promises in Templates
  11. What’s Next
  12. More Svelte and SvelteKit Guides

1. What is Svelte?

Svelte is an open-source component-based JavaScript framework that allows us to create user interfaces by writing composable components.

A Svelte app is compiled into lean JavaScript during the build process. When state changes, Svelte updates the parts of the page that depend on that state. Unlike frameworks that do most of their work through a virtual DOM at runtime, Svelte moves much of that work into its compiler.

We can use Svelte to build a small standalone component or a complete web app. For routing, server rendering, form actions, and full-stack applications, the official application framework is SvelteKit.

2. Get started with Svelte

To start with Svelte, first of all, you should install a current LTS version of Node.js, if you don’t have it already.

There are several ways to install and use Svelte on your machine. In this article, we’re going to use the official sv command to create a SvelteKit project.

npx sv create first-app

The command will ask you to choose a project template, JavaScript or TypeScript, and any optional tools that you want to add. For your first app, the minimal template is a good place to start.

Once the project is ready, go to the new directory and start the development server:

cd first-app
npm run dev

The terminal will show the local address for your app. By default, it is normally http://localhost:5173. You can also run npm run dev -- --open to open it in your browser.

3. Svelte Components

Components are the building blocks of any Svelte application. Each individual component represents a small piece of an application, such as a message, a navigation bar, or a login form.

Svelte components contain HTML, CSS, and JavaScript. These components are written in .svelte files.

Here is a quick example of a Svelte file:

<!-- Message.svelte -->

<script>
  const message = 'Hello World';
</script>

<h1>{message}</h1>

<style>
  h1 {
    color: red;
  }
</style>

The <script> block contains the component logic, the markup describes the interface, and the <style> block contains CSS that is scoped to this component by default.

3.1 Nested Components

It will be impractical to use a single component in an application. In Svelte, a component can be imported into another component.

Example: we can import Message.svelte in App.svelte:

<!-- App.svelte -->

<script>
  import Message from './Message.svelte';
</script>

<Message />

3.2 Component Props

To use a component, we don’t have to write any extra export code because a Svelte component can be imported by default.

On the other hand, if we want a component to receive data from its parent, we can declare props with the $props rune. We can also assign a default value to a prop.

<!-- Message.svelte -->

<script>
  let { message = 'Hello World' } = $props();
</script>

<h1>{message}</h1>

Example: how to pass the prop from the parent:

<!-- App.svelte -->

<script>
  import Message from './Message.svelte';
</script>

<Message message="I'm learning Svelte" />

4. Svelte Reactivity

Whenever the state of a component changes, Svelte automatically updates the parts of the DOM that use that state.

In Svelte 5, reactive state is declared with the $state rune. The value still behaves like a normal JavaScript value, so we can update it directly.

Example: updating a variable by clicking a button:

<script>
  let count = $state(0);

  function updateCount() {
    count += 1;
  }
</script>

<button onclick={updateCount}>Click Count</button>

<p>{count}</p>

4.1 Derived State

Often, in an app, we need to compute one value from other values. For example, a total may be derived from two different input values.

We can declare derived state with the $derived rune:

<script>
  let x = $state(5);
  let y = $state(3);

  let total = $derived(x + y);
</script>

<button onclick={() => x += 1}>Change x</button>
<button onclick={() => y += 1}>Change y</button>

<p>{total}</p>

In this example, we have created a derived value called total. Whenever x or y changes, Svelte updates total automatically.

4.2 Effects

Sometimes, we need to run a side effect when reactive state changes. For example, we may need to update a third-party library or write a value to local storage.

For this work, Svelte provides the $effect rune:

<script>
  let count = $state(0);

  $effect(() => {
    console.log('Count:', count);
  });
</script>

<button onclick={() => count += 1}>Click Count</button>

Use $derived for calculated values and $effect for side effects. This makes it easier to understand why each reactive block is running.

5. State Management in Svelte

We have already seen that handling the state of a single component is extremely easy in Svelte.

Svelte provides multiple ways to pass the state across different components. Let’s break them down:

5.1 Props

In Svelte, props are used to pass state downward through the component tree. This is a common strategy among modern UI frameworks.

We can pass either a variable or an expression through a prop:

<Component prop1={valueA} prop2={valueA + valueB} />

Whenever a prop changes, Svelte updates the parts of the child component that depend on it.

5.2 Callback Props and Events

Sometimes, a child component needs to tell its parent that something happened. In Svelte 5, the simplest approach is to pass a callback function as a prop.

Here is the parent component:

<!-- App.svelte -->

<script>
  import Child from './Child.svelte';

  function handleData(data) {
    console.log(data);
  }
</script>

<Child onData={handleData} />

The child receives the callback as a prop and calls it when the button is clicked:

<!-- Child.svelte -->

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

  function handleClick() {
    onData({ message: 'some data' });
  }
</script>

<button onclick={handleClick}>Send data</button>

Older Svelte code may use createEventDispatcher and on: event directives. They still explain many older examples, but callback props and event attributes are the current style for new Svelte 5 code.

5.3 Context

There are a few cases where props are not practical to use. For example, in a large application, two components may be so distant in the component tree that we would have to pass the same prop through several components.

To solve this issue, Svelte provides a context API. It is useful when a parent needs to make a value available to its descendants without passing it through every level.

There are two main functions provided by the context API:

5.3.1 setContext

We can set a value in the context:

<!-- First.svelte -->

<script>
  import { setContext } from 'svelte';

  setContext('number', 111);
</script>

5.3.2 getContext

getContext() is used to retrieve the value that belongs to the closest parent component with the specified key.

Example: get the context from First.svelte in Second.svelte:

<!-- Second.svelte -->

<script>
  import { getContext } from 'svelte';

  const myNumber = getContext('number');
</script>

<p>{myNumber}</p>

5.4 Shared State with Runes

In Svelte 5, runes can also be used in .svelte.js or .svelte.ts files. This gives us a simple way to share reactive state across components.

// app-state.svelte.js
export const appState = $state({
  message: 'Hello World',
  count: 0
});

We can import the object into a component and update its properties:

<script>
  import { appState } from './app-state.svelte.js';
</script>

<button onclick={() => appState.count += 1}>
  {appState.count}
</button>

5.5 Stores

Svelte stores are still supported. They are useful when we are working with an existing store-based codebase or a library that follows the store contract.

There are three common types of stores:

5.5.1 Writable Stores

First of all, to create a store, we need to import writable from svelte/store. After that, we can create a store while passing a default value.

<script>
  import { writable } from 'svelte/store';

  const count = writable(0);

  function handleClick() {
    count.update((value) => value + 1);
  }
</script>

<button onclick={handleClick}>Click Count</button>
<p>{$count}</p>

A writable store provides set, update, and subscribe methods. Inside a component, the $count syntax automatically subscribes to the store and gives us its current value.

5.5.2 Readable Stores

Readable stores can be updated by the function that creates them, but code using the store does not receive a public set or update method.

Here is an example:

import { readable } from 'svelte/store';

export const count = readable(0, (set) => {
  const timer = setTimeout(() => {
    set(1);
  }, 1000);

  return () => clearTimeout(timer);
});

5.5.3 Derived Stores

Derived stores allow us to create a new store value from one or more existing stores.

import { derived, writable } from 'svelte/store';

export const message = writable('Hello World!');

export const greeting = derived(message, ($message) => {
  return $message + ' My name is John';
});

For new Svelte 5 code, shared $state and $derived values often replace custom stores. Stores remain useful when their subscription contract is exactly what we need.

6. Svelte Snippets

In Svelte, components can be composed together using snippets. In simple words, we can pass a reusable block of markup into a component.

The content placed between a component’s opening and closing tags becomes a children snippet. The child component can render it using {@render children()}.

<!-- Card.svelte -->

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

<article>
  <h2>{title}</h2>

  {#if children}
    {@render children()}
  {:else}
    <p>No description provided.</p>
  {/if}
</article>

Let’s use Card.svelte in a new component:

<!-- App.svelte -->

<script>
  import Card from './Card.svelte';

  const books = [
    {
      title: 'Harry Potter',
      description: 'A fantasy adventure'
    },
    {
      title: 'Lord of the Rings',
      description: 'A journey through Middle-earth'
    }
  ];
</script>

{#each books as book}
  <Card title={book.title}>
    <p>{book.description}</p>
  </Card>
{/each}

We can also declare our own named snippet with {#snippet} and render it more than once:

{#snippet greeting(name)}
  <p>Hello {name}</p>
{/snippet}

{@render greeting('Jack')}
{@render greeting('John')}

Older Svelte components use <slot> for this job. Svelte 5 uses snippets and render tags for new code because they are more flexible and can accept parameters.

7. Svelte Bindings

In Svelte, we can create a two-way binding between data and the UI. It is a common technique among modern web frameworks.

7.1 bind:value

Let’s take a state variable and bind it to a form field using bind:value:

<script>
  let message = $state('');
</script>

<input bind:value={message} />
<p>{message}</p>

Whenever the input field or message changes, Svelte updates the other place.

The bound value has to be assignable, so we use let rather than const.

bind:value also works with a <select> element to get the selected value:

<script>
  let selected = $state('1');
</script>

<select bind:value={selected}>
  <option value="1">1</option>
  <option value="2">2</option>
  <option value="3">3</option>
</select>

7.2 bind:checked

bind:checked is used to bind a boolean value to the checked state of an element:

<script>
  let isChecked = $state(false);
</script>

<input type="checkbox" bind:checked={isChecked} />

7.3 bind:group

In Svelte, bind:group is useful with checkboxes and radio buttons.

We can associate one value with a group of radio buttons and an array with a group of checkboxes:

<script>
  let ownedBooks = $state('1');
  let bookTitles = $state([]);
</script>

<!-- Grouped radio inputs are mutually exclusive -->
<input type="radio" bind:group={ownedBooks} value="1" />
<input type="radio" bind:group={ownedBooks} value="2" />
<input type="radio" bind:group={ownedBooks} value="3" />

<!-- Grouped checkbox inputs populate an array -->
<input type="checkbox" bind:group={bookTitles} value="Harry Potter" />
<input type="checkbox" bind:group={bookTitles} value="Lord of the Rings" />
<input type="checkbox" bind:group={bookTitles} value="The Hunger Games" />

8. Svelte Lifecycle Hooks

In Svelte 5, the component lifecycle has two main parts: creation and destruction. Updates in between are handled by the reactive parts of the component that depend on the changed state.

Lifecycle hooks are useful when we want to start or clean up work as a component enters or leaves the page. They are also useful when we need to connect a browser API or a third-party JavaScript library.

8.1 onMount

Svelte runs the onMount callback after a component has been mounted to the DOM. It does not run while the component is being rendered on the server.

<script>
  import { onMount } from 'svelte';

  onMount(() => {
    console.log('The component has mounted');
  });
</script>

If we need to fetch data, we can start an asynchronous function from inside a synchronous onMount callback.

8.2 Cleanup and onDestroy

If onMount returns a function, Svelte runs that function when the component is unmounted. This is a convenient way to stop a timer or remove an event listener.

<script>
  import { onMount } from 'svelte';

  let seconds = $state(0);

  onMount(() => {
    const timer = setInterval(() => {
      seconds += 1;
    }, 1000);

    return () => clearInterval(timer);
  });
</script>

<p>{seconds} seconds</p>

Svelte also provides onDestroy when we want to register cleanup separately:

<script>
  import { onDestroy } from 'svelte';

  const controller = new AbortController();

  onDestroy(() => {
    controller.abort();
  });
</script>

8.3 Reacting to Updates

Older Svelte guides use beforeUpdate and afterUpdate. These hooks are deprecated in Svelte 5 and are not available in components that use runes.

For current code, use $effect.pre when work must run before a reactive DOM update, and use tick when we need to wait until pending DOM updates have finished.

<script>
  import { tick } from 'svelte';

  let count = $state(0);

  $effect.pre(() => {
    console.log('The next count is', count);

    tick().then(() => {
      console.log('The DOM update is complete');
    });
  });
</script>

<button onclick={() => count += 1}>{count}</button>

9. Conditional Logic in Svelte Templates

Just like all the major templating languages, in Svelte, we can use conditional blocks and loops. This logic is really useful when we want to create our interface in a specific way.

9.1 Conditional Structure

Let’s start with if.

To use an if block, we have to start with {#if} and close it using {/if}.

Here is an example:

<script>
  const color = 'white';
</script>

{#if color === 'white'}
  <p>Color is white</p>
{/if}

We can also use the {:else} condition:

<script>
  const color = 'black';
</script>

{#if color === 'white'}
  <p>Color is white</p>
{:else}
  <p>Color is black</p>
{/if}

Let’s add an else if condition:

<script>
  const color = 'blue';
</script>

{#if color === 'white'}
  <p>Color is white</p>
{:else if color === 'blue'}
  <p>Color is blue</p>
{:else}
  <p>Color is black</p>
{/if}

9.2 Loops

In Svelte, we can create a loop using the {#each}{/each} syntax:

<script>
  const names = ['Jack', 'John'];
</script>

{#each names as name}
  <li>{name}</li>
{/each}

We can also get the index of the iteration. The index always starts from 0.

<script>
  const names = ['Jack', 'John'];
</script>

{#each names as name, index}
  <li>{index}: {name}</li>
{/each}

In templates, we can pass a list of objects. Here is an example:

<script>
  const people = [
    { id: 1, name: 'Jack' },
    { id: 2, name: 'John' }
  ];
</script>

{#each people as person (person.id)}
  <li>{person.id}: {person.name}</li>
{/each}

The value in parentheses is a key. It helps Svelte identify each item when the list changes.

10. Promises in Templates

Promises are really useful when working with asynchronous events in JavaScript.

In Svelte, we can structure our templates according to the different states of a promise: pending, fulfilled, and rejected.

Let’s see how it works.

  • We can use an {#await} block while a promise is pending.
  • Once the promise is fulfilled, the result is passed to the {:then} block.
  • To handle a rejected promise, we can use the {:catch} block.
<script>
  const randomPromise = new Promise((resolve) => {
    setTimeout(() => {
      resolve('Hello World');
    }, 2000);
  });
</script>

{#await randomPromise}
  <p>Waiting for the promise to resolve...</p>
{:then data}
  <p>{data}</p>
{:catch error}
  <p>An error occurred: {error.message}</p>
{/await}

11. What’s Next

I hope this introduction was helpful to you to understand what Svelte is and what you can do with it.

If you want to learn more about Svelte, continue with the official Svelte tutorial and keep the Svelte documentation nearby when you build your first project. 🙂

If you are updating an older project, the Svelte 5 migration guide explains how legacy reactivity, events, and slots map to the current APIs.

12. More Svelte and SvelteKit Guides

Once you understand the Svelte fundamentals, these RavensMove guides cover practical setup and deployment tasks:



Frequently Asked Questions

1. Is Svelte better than React?
It depends on the project and the team. Svelte uses a compiler and gives us a concise component model, while React has a larger ecosystem and a very large developer community. The better choice is the one that matches the application, the available libraries, and the experience of the people maintaining it.
2. What is the use of Svelte JS?
Svelte is used to build interactive user interfaces, standalone components, and complete web apps. When we need routing, server rendering, form actions, or server-side code, we can build the application with SvelteKit.
3. Is Svelte still relevant?
Yes. Svelte 5 is the current generation of the framework, and the official ecosystem includes SvelteKit, the Svelte CLI, an interactive tutorial, and actively maintained documentation. It remains a practical option for modern web applications.