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
- What is Svelte?
- Get Started with Svelte
- Svelte Components
- Svelte Reactivity
- State Management in Svelte
- Svelte Snippets
- Svelte Bindings
- Svelte Lifecycle Hooks
- Conditional Logic in Svelte Templates
- Promises in Templates
- What’s Next
- 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:
- How to import CSS from node_modules in Svelte
- Focus dynamically added input fields in Svelte
- Install Bootstrap in SvelteKit
- How to Deploy a SvelteKit App to Firebase
- How to Dockerize A Svelte App
- Build a Chrome Extension with Svelte
Frequently Asked Questions
Related Posts
- Deploy SvelteKit to Firebase Hosting (Static Guide)
- Bootstrap 5 in SvelteKit: Install and Import CSS
- Build a Chrome Extension with Svelte
- Focus a Dynamic Input Field in Svelte
- How to Install Node.js and NPM on Ubuntu
- AWS Lambda Function with Response Streaming using Node.js
- How to Dockerize A Svelte App
- How to import CSS from node_modules in Svelte
- How to fix Nginx 403 Forbidden Error
- Linked List Implementation in C++ - A Step by Step Guide