--- title: The Complete Svelte 5 Guide description: The ultimate guide for the most beloved JavaScript framework. slug: learn-svelte published: '2025-8-14' category: svelte --- ## Table of Contents ## What is Svelte? If we look at the definition from the [Svelte](https://svelte.dev/) website, it says: > Svelte is a UI framework that uses a compiler to let you write breathtakingly concise components that do minimal work in the browser, using languages you already know โ€” HTML, CSS and JavaScript. Because Svelte is a compiled language, it can wield the same syntax of a language that's not great at making user interfaces like JavaScript and change the semantics for a better developer experience: ```svelte:App.svelte

{count}

``` You might think how Svelte does some crazy compiler stuff under the hood to make this work, but the output is human readable JavaScript: ```ts:output function App($$anchor) { // create signal let count = state(0) // update signal setInterval(() => set(count, get(count) + 1), 1000) // create element var p = from_html(`

`) var text = child(p, true) // update DOM when `count` changes template_effect(() => set_text(text, get(count))) // add to DOM append($$anchor, p) } ``` Svelte's reactivity is based on [signals](https://www.youtube.com/watch?v=1TSLEzNzGQM), so there's nothing magical about it โ€” you could write Svelte code without a compiler, but it would be tedious like writing [JSX](https://react.dev/learn/writing-markup-with-jsx) by hand using functions. Just by reading the output code, you can start to understand how Svelte works. There's no virtual DOM, or rerendering the component when state updates like in React โ€” Svelte only updates the part of the DOM that changed. This is what **"does minimal work in the browser"** means! Svelte also has a more opinionated application framework called [SvelteKit](https://svelte.dev/docs/kit/introduction) (equivalent to [Next.js](https://nextjs.org/) for React) if you need routing, server-side rendering, adapters to deploy to different platforms and so on. ## Try Svelte You can try Svelte in the browser using the [Svelte Playground](https://svelte.dev/playground) and follow along without having to set up anything. Some of the examples use browser APIs like the Web Storage API that won't work in the Svelte Playground, but you can use an online IDE like SvelteLab. If you're a creature of comfort and prefer your development environment, you can scaffold a Vite project and pick Svelte as the option from the CLI if you run `npm create vite@latest` in a terminal โ€” you're going to need [Node.js](https://nodejs.org/) for that. I also recommend using the [Svelte for VS Code extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode) for syntax highlighting and code completion, or a similar extension for your editor. ## TypeScript Aside [TypeScript](https://www.typescriptlang.org/) has become table stakes when it comes to frontend development. For that reason, the examples are going to use TypeScript, but you can use JavaScript if you prefer. If you're unfamiliar with TypeScript, code after `:` usually represents a type. You can omit the types and your code will work: ```ts:example // TypeScript ๐Ÿ‘๏ธ let items: string[] = [...] // JavaScript ๐Ÿ‘๏ธ let items = [...] ``` Some developers prefer writing JavaScript with [JSDoc](https://jsdoc.app/) comments because it gives you the same benefits of TypeScript at the cost of a more verbose syntax: ```ts:example /** * This is a list of items. * @type {string[]} */ let items = [...] ``` That is completely up to you! ## Single File Components In Svelte, files ending with `.svelte` are called **single file components** because they contain the JavaScript, HTML, and CSS in a single file. Here's an example of a Svelte component: ```svelte:App.svelte

{title}

``` A Svelte component can only have one top-level `

{title as string}

``` Later we're going to learn how you can even define values inside your markup, which can be helpful in some cases. ## Markup Poetry In Svelte, anything that's outside the `

There's {banana} {banana === 1 ? 'banana' : 'bananas'} left

``` Later we're going to learn about logic blocks like `{#if ...}` and `{#each ...}` to conditionally render content. Tags with lowercase names are treated like regular HTML elements by Svelte and accept normal attributes: ```svelte:App.svelte Person dancing ``` You can pass values to attributes using curly braces: ```svelte:App.svelte {alt} ``` If the attribute name and value are the same, you can use a shorthand attribute: ```svelte:App.svelte {alt} ``` Attributes can have expressions inside the curly braces: ```svelte:App.svelte ``` You can spread objects on elements: ```svelte:App.svelte ``` To conditionally render attributes, use `null` or `undefined` instead of `&&` for short-circuit evaluation and empty strings: ```svelte:App.svelte ``` ## Component Styles There are many ways you can style a Svelte component. ๐Ÿ’… I've heard people love inline styles with [Tailwind CSS](https://tailwindcss.com/), so you could just use the `style` tag...I'm joking! ๐Ÿ˜„ That being said, the `style` tag can be useful. You can use the `style` attribute like in regular HTML, but Svelte also has a shorthand `style:` directive you can use. The only thing you can't pass is an object: ```svelte:App.svelte

Svelte

Svelte

Svelte

``` You can even add `important` like `style:color|important` to override styles. The `style:` directive is also great for CSS custom properties: ```svelte:App.svelte

Svelte

Svelte

``` ### Scoped Styles Fortunately, you're not stuck using the `style` attribute. Most of the time, you're going to use the `style` block to define styles in your component. Those styles are scoped to the component by default: ```svelte:App.svelte

Svelte

``` Scoped styles are unique to that component and don't affect styles in other components. If you're using the Svelte playground, you can open the CSS output tab to view the generated CSS: ```css:output /* uniquely generated class name */ h1.svelte-ep2x9j { color: orangered; } ``` If you want to define global styles for your app, you can import a CSS stylesheet at the root of your app: ```ts:main.ts {4} // inside a Vite project import { mount } from 'svelte' import App from './App.svelte' import './app.css' const app = mount(App, { target: document.getElementById('app')! }) export default app ``` You can also define global styles in components. This is useful if you have content from a content management system (CMS) that you have no control over. Svelte has to "see" the styles in the component, so it doesn't know they exist and warns you about removing unusued styles: ```svelte:App.svelte {15-17,20-22}
{@html content}
``` The @html tag is used to render raw HTML in Svelte components. If you don't control the content, always sanitize user input to prevent XSS attacks. In that case, you can make the styles global by using the `:global(selector)` modifier: ```svelte:App.svelte {4-6,8-10} ``` Having to use `:global` on every selector is tedious! Thankfully, you can nest global styles inside a `:global { ... }` block: ```svelte:App.svelte {3} ``` You can also have "global scoped styles" where the styles inside the `:global` block are scoped to the class: ```svelte:App.svelte {3} ``` Here's the compiled CSS output: ```css:output article.svelte-ju1yn8 { h1 { text-transform: capitalize; } p { text-wrap: pretty; } } ``` Keyframe animations are also scoped to the component. If you want to make them global, you have to prepend the keyframe name with `-global-`. The `-global-` part is removed when compiled, so you can reference the keyframe name in your app: ```svelte:App.svelte ``` You can use different [preprocessors](https://svelte.dev/docs/kit/integrations#vitePreprocess) like [PostCSS](https://postcss.org/) or [SCSS](https://sass-lang.com/) by simply adding the `lang` attribute to the ` ``` These days you probably don't need SCSS anymore, since a lot of features such as nesting and CSS variables are supported by CSS. ### Dynamic Classes You can use an expression to apply a dynamic class, but it's tedious and easy to make mistakes: ```svelte:App.svelte {2,7,15-17} ``` Thankfully, Svelte can helps us out here. You can use the `class:` directive to conditionally apply a class: ```svelte:App.svelte ๐Ÿ‘ˆ๏ธ ``` You can also pass an object, array, or both to the `class` attribute and Svelte is going to use [clsx](https://github.com/lukeed/clsx) under the hood to merge the classes: ```svelte:App.svelte ๐Ÿ‘ˆ๏ธ ๐Ÿ‘ˆ๏ธ ๐Ÿ‘ˆ๏ธ ``` If you're using Tailwind, this is very useful when you need to apply a bunch of classes: ```svelte:App.svelte ๐Ÿ‘ˆ๏ธ ``` Also consider using [data attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/How_to/Use_data_attributes) for explicit transition states for easier orchestration, instead of using a bunch of classes: ```svelte:App.svelte {2,5,12-14,16-18} ๐Ÿ‘ˆ๏ธ ``` ## Svelte Reactivity In the context of JavaScript frameworks, **application state** refers to values that are essential to your application working and cause the framework to update the UI when changed. Let's look at a counter example: ```svelte:App.svelte ``` The Svelte compiler knows that you're trying to update the `count` value and warns you because it's not reactive: > `count` is updated, but is not declared with `$state(...)`. Changing its value will not correctly trigger updates. This brings us to our first Svelte rune โ€” the `$state` rune. ## Reactive State The `$state` rune marks a variable as reactive. Svelte's reactivity is based on **assignments**. To update the UI, you just assign a new value to a reactive variable: ```svelte:App.svelte {3,7} ``` You can open the developer tools and see that Svelte only updated the part of the DOM that changed. The example uses count += 1 to emphasize assignment, but using count++ to increment the value also works. The `$state(...)` syntax is called a **rune** and is part of the language. It looks like a function, but it's only a hint for the Svelte compiler to know what to do with it. This also means as far as TypeScript is concerned, it's just a function: ```ts:example let value = $state(...) ``` The three main runes we're going to learn about are the `$state`, `$derived`, and `$effect` rune. ## Deeply Reactive State If you pass an array, or object to `$state` it becomes a deeply reactive [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). This lets Svelte perform granular updates when you read or write properties and avoids mutating the state directly. For example, changing `editor.content` is going to update the UI in every place where it's used: ```svelte:App.svelte {2-5,10,14} {@html editor.content} ``` ### $state.raw You might not want deeply reactive state, where pushing to an array or updating the object would cause an update. In that case, you can use `$state.raw` so state only updates when you reassign it: ```svelte:App.svelte {3-6,13,16-19} {@html editor.content} ``` ### $state.snapshot Because proxied state is deeply reactive, you could change it on accident when you pass it around, or run into a problem with some API that doesn't expect it. In that case, you can use `$state.snapshot` to get the normal value from the Proxy: ```ts:editor.ts function saveEditorState(editor) { // ๐Ÿ’ฃ๏ธ oops! it doesn't like a Proxy object... const editorState = structuredClone(editor) // ๐Ÿ‘๏ธ normal object const editorState = structuredClone($state.snapshot(editor)) } // later saveEditorState(editor) ``` Svelte uses $state.snapshot when you console.log deeply reactive values for convenience. ### Destructuring You can destructure deep state where you defined it โ€” but if you destructure it anywhere else โ€” it loses reactivity because it's just JavaScript, so the values are evaluated when you destructure them: ```svelte:App.svelte {@html content} ``` If you want to do this, you can use derived state! ## Derived State You can derive state from other state using the `$derived` rune and it's going to reactively update: ```svelte:App.svelte {4}

{count} * {factor} = {result}

``` ### Deriveds Only Update When They Change Derived values **only run when they're read** and are **lazy evaluted** which means they only update when they change, and **not** when their dependencies change to avoid unnecessary work. Here even if `max` depends on `count`, it only updates when `max` updates: ```svelte:App.svelte {3,6,9} ``` The $inspect rune only runs in development and is great for seeing state updates and debugging. ### Derived Dependency Tracking You can pass a function with state to a derived without losing reactivity: ```svelte:App.svelte {3,5-7} ``` This might sound like magic, but the only magic here is the system of signals and runtime reactivity! ๐Ÿช„ In a later section, we're going to learn how this exactly works. The reason you don't have to pass state to the function โ€” unless you want to be explicit โ€” is because signals only care where they're read, as highlighted in the compiled output. Passing state as argument: ```ts:output {1} let disabled = derived(() => limit(get(count))) // ๐Ÿ“– function limit(count) { return count > 4 } ``` Not passing any arguments: ```ts:output {4} let disabled = derived(limit()) function limit() { return get(count) > 4 // ๐Ÿ“– } ``` The only thing that matters is that `count` is read and tracked inside of an effect, so the `limit` function reruns when it updates. ### $derived.by The `$derived` rune only accepts an expression by default, but you can use the `$derived.by` rune for more complex derivations: ```svelte:App.svelte {6-12}

Total: {total}

``` Svelte recommends you keep deriveds free of side-effects and you can't update state inside of deriveds to protect you from unintended side-effects: ```svelte:App.svelte {5} ``` ### Destructuring From Deriveds Going back to the previous example, you can also use derived state to keep reactivity when using destructuring: ```svelte:App.svelte {8,11} {@html content} ``` ## Effects The last rune in the holy trinity of reactivity in Svelte you should know about is the `$effect` rune. Effects are functions that run **when the component is added** to the DOM and **when their dependencies change**. State that is **read** inside of an effect will be tracked. Here `count` is going to be logged when it updates, since it's read inside of the effect and tracked as a dependency: ```svelte:App.svelte {2,6} ``` Values are only tracked if they're **read** โ€” here if `condition` is `true`, then `condition` and `count` are going to be tracked, but if `condition` is false, then the effect only reruns when `condition` changes: ```svelte:App.svelte {3,7,9} ``` Use the $inspect rune instead of effects to log when a reactive value updates. You can return a function from the effect callback, which reruns when the effect **dependencies change**, or when the component is **removed** from the DOM. Values that are read **asynchronously** like inside promises and timers are **not tracked** inside effects: ```svelte:App.svelte {9} {count} ``` Svelte provides an `untrack` function if you don't want state to be tracked: ```svelte:App.svelte {2,9} ``` ### Effect Dependency Tracking When it comes to deeply reactive state, effects only rerun when the object it reads changes and not its properties: ```svelte:App.svelte ``` There are exceptions to the rule! If you use `JSON.stringify` or `$state.snapshot`, then everything is tracked: ```svelte:App.svelte {6,10} ``` ### When Not To Use Effects In general, you should **always** avoid effects and **never use effects to synchronize state**, because Svelte queues your effects to ensure they run in the correct order and runs them last. Using effects to synchronize state can cause unexpected behavior like state being out of sync: ```svelte:App.svelte {7,12-13} ``` **Always derive state** when you can instead: ```svelte:App.svelte {3,7-8} ``` Deriveds are effects under the hood, but they rerun immediately when their dependencies change. If you want to do something when the component is added to the DOM and don't care about tracking state, you can use the `onMount` lifecycle function instead of an effect: ```svelte:App.svelte ``` Avoid passing async callbacks to onMount and $effect as their cleanup won't run. You can use async functions, or an IIFE instead. ### When To Use Effects **Effects should be a last resort** when you have to synchronize with an external system that doesn't understand Svelte's reactivity. They should only be used for side-effects like fetching data from an API, or working with the DOM directly. In this example, we're using the Pokemon API and `getAbortSignal` from Svelte to avoid making a bunch of requests when searching for a Pokemon: ```svelte:App.svelte {17-21} pokemon = (e.target as HTMLInputElement).value} /> {pokemon} ``` ### $effect.pre Your effects run after the DOM updates in a [microtask](https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide), but sometimes you might need to do work before the DOM updates like measuring an element, or scroll position โ€” in that case, you can use the `$effect.pre` rune. Let's look at an example that uses the [GSAP Flip plugin](https://gsap.com/docs/v3/Plugins/Flip/) to animate changes in the DOM and needs to measure the position, size, and rotation of elements before, and after the DOM update. In this example, we measure the elements before the DOM updates, and use `tick` to wait for the DOM update: ```svelte:App.svelte {10-20}
{#each items as item (item)}
{item}
{/each}
``` The tick function is a lifecyle function that uses the queueMicrotask method to schedule a task to run in the next microtask when all the work is done, and before the DOM updates. ## State In Functions And Classes So far, we only used runes at the top-level of the component, but you can use state, deriveds, and effects inside functions and classes. You can use runes in a JavaScript module by using the `.svelte.js` or `.svelte.ts` extension to tell Svelte that it's a special file, so it doesn't have to check every file for runes. Here's a `createCounter` function that holds and returns the `count` state: ```ts:counter.svelte.ts export function createCounter(initial: number) { let count = $state(initial) return { get count() { return count }, set count(v) { count = v } } } ``` Here's how it's used inside of a Svelte component: ```svelte:App.svelte {counter.count} ``` ### Reactive Properties You're probably wondering, what's the deal with the `get` and `set` methods? Those are called **getters and setters**, and they create **accessor properties** which let you define custom behavior when you read and write to a property. They're just part of JavaScript, and you could use functions instead: ```ts:counter.svelte.ts export function createCounter(initial: number) { let count = $state(initial) return { count() { return count }, setCount(v: number) { count = v } } } ``` You could return a tuple instead to make the API nicer and destructure the read and write functions like `let [count, setCount] = createCounter(0)`. As you can see, the syntax is not as nice compared to using accessors, since you have to use functions everywhere: ```svelte:App.svelte ``` ### Reactive Containers The accessor syntax looks a lot nicer! ๐Ÿ˜„ You might be wondering, can't you just return state from the function? ```ts:counter.svelte.ts export function createCounter(initial: number) { let count = $state(initial) // โ›”๏ธ this doesn't work return { count } } ``` The reason this doesn't work is because **state is just a regular value** and **not** some magic reactive container. If you want something like that, you could return deeply reactive proxied state: ```ts:counter.svelte.ts export function createCounter(initial: number) { let counter = $state({ count: initial }) // ๐Ÿ‘๏ธ proxied state return counter } ``` You can create a reactive container if you want: ```ts:counter.svelte.ts {2-5,9} // reactive container utility function reactive(value: T) { let state = $state(value) return state } function createCounter(initial: number) { // reactive container let counter = reactive({ count: initial }) return { counter } } ``` Even destructuring works, since `counter` is not just a regular value: ```svelte:App.svelte {4} ``` That seems super useful, so why doesn't Svelte provide this utility? It's mostly because it's a few lines of code, but another reason is **classes**. If you use state inside classes, you get extra benefits which you don't get using functions. Svelte turns any class fields declared with state into private fields with matching `get`/`set` methods, unless you declare them yourself: ```ts:counter.svelte.ts {4} export class Counter { constructor(initial: number) { // turned into `get`/`set` methods this.count = $state(initial) } increment() { this.count++ } decrement() { this.count-- } } ``` If you look at the output, you would see something like this: ```ts:output class Counter { #count get count() { ... } set count(v) { ... } } ``` There's only one gotcha with classes and it's how `this` works. For example, using a method like `counter.increment` inside `onclick` doesn't work, because `this` refers to where it was called: ```svelte:App.svelte {counter.count} ``` You can see it for yourself: ```ts:counter.svelte.ts increment() { console.log(this) // button this.count++ } decrement() { console.log(this) // button this.count-- } ``` You either have to pass an anonymous function like `() => counter.increment()` to `onclick`, or define the methods using arrow functions that don't bind their own `this`: ```ts:counter.svelte.ts increment = () => console.log(this) // class this.current++ } decrement = () => { console.log(this) // class this.current-- } ``` The only downside with arrow functions is that you're creating a new function every time time you call it, but everything works as expected. ### Passing State Into Functions And Classes Because state is a regular value, it loses reactivity when you pass it into a function or a class. In this example, we pass `count` into `Doubler` to double the value when `count` updates. However, it's not reactive since `count` is a regular value: ```svelte:App.svelte {2-6,9} ``` Svelte even gives you a warning with a hint: > This reference only captures the initial value of `count`. Did you mean to reference it inside a closure instead? To get the latest `count` value, we can pass a function instead: ```svelte:App.svelte {3-5,9} ``` Also, we already have a reactive `Counter` class we can use: ```svelte:App.svelte {2-6,9-11,14-15} ``` Runes are **reactive primitives** that give you the flexibility to create your own reactivity system. ## Reactive Global State Creating global reactive state in Svelte is simple as exporting deep state from a module, like a config which can be used across your app: ```ts:config.svelte.ts interface Config { theme: 'light' | 'dark' } export const config = $state({ theme: 'dark' }) export function toggleTheme() { config.theme = config.theme === 'light' ? 'dark' : 'light' } ``` ```svelte:App.svelte ``` You could use a function, or a class for the config: ```ts:config.svelte.ts type Themes = 'light' | 'dark' class Config { theme = $state('dark') toggleTheme() { this.theme = this.theme === 'light' ? 'dark' : 'light' } } export const config = new Config() ``` It doesn't matter if you use functions or classes, as long as you understand how Svelte reactivity works. ## Why You Should Avoid Effects I don't want to scare you from using effects. Honestly, it's not a big deal if you **sometimes** use effects when you shouldn't. It's unlikely your app would be worse just by using effects โ€” the actual problem is that it's easy to overcomplicate your code with effects, because it seems like the right thing to do. In this example, we're using the [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API) to read and write the `counter` value each time it updates. Hey, that's a side-effect! Using an effect seems resonable: ```ts:counter.svelte.ts class Counter { constructor(initial: number) { this.count = $state(initial) $effect(() => { const savedCount = localStorage.getItem('count') if (savedCount) this.count = parseInt(savedCount) }) $effect(() => { localStorage.setItem('count', this.count.toString()) }) } } ``` The problem only arises if you create the counter outside the component initialization phase (in a separate module, or inside of an event handler): ```ts:counter.svelte.ts export const counter = new Counter(0) ``` Oops! Immediately, there's an `effect_orphan` error: > `$effect` can only be used inside an effect (e.g. during component initialisation). Your entire app is a root effect with other nested effects, so Svelte can run the teardown logic for them when the component is removed โ€” in this case, you're trying to create an effect outside that root effect, so Svelte can't keep track of it. Svelte provides an advanced `$effect.root` rune to create your own root effect, but now you have to run the cleanup manually: ```ts:counter.svelte.ts {2,8-19,22-24} class Counter { #cleanup constructor(initial: number) { this.count = $state(initial) // manual cleanup ๐Ÿ˜ฎโ€๐Ÿ’จ this.cleanup = $effect.root(() => { $effect(() => { const savedCount = localStorage.getItem('count') if (savedCount) this.count = parseInt(savedCount) }) $effect(() => { localStorage.setItem('count', this.count.toString()) }) return () => console.log('๐Ÿงน cleanup') }) } destroy() { this.#cleanup() } } ``` Awkward! ๐Ÿ˜„ Then you learn about the `$effect.tracking` rune, used to know if you're inside a **tracking context** like the effect in your template, so maybe that's the solution? ```ts:counter.svelte.ts {5-14} class Counter { constructor(initial: number) { this.count = $state(initial) if ($effect.tracking()) { $effect(() => { const savedCount = localStorage.getItem('count') if (savedCount) this.count = parseInt(savedCount) }) $effect(() => { localStorage.setItem('count', this.count.toString()) }) } } } ``` But there's **another** problem! The effect is never going to run when the counter is created, because you're not inside a tracking context. ๐Ÿ˜ฉ It would make more sense to move the effect where you read the value โ€” this way, it's read inside of a tracking context like the template effect: ```ts:counter.svelte.ts {7-12,17} export class Counter { constructor(initial: number) { this.#count = $state(initial) } get count() { if ($effect.tracking()) { $effect(() => { const savedCount = localStorage.getItem('count') if (savedCount) this.#count = parseInt(savedCount) }) } return this.#count } set count(v: number) { localStorage.setItem('count', v.toString()) this.#count = v } } ``` There's **another** problem. Each time we read the value, we're creating an effect! ๐Ÿ˜จ That's a simple fix โ€” we can use a variable to track if we already ran the effect: ```ts:counter.svelte.ts {2,11,14} export class Counter { #first = true constructor(initial: number) { this.#count = $state(initial) } get count() { if ($effect.tracking()) { $effect(() => { if (!this.#first) return const savedCount = localStorage.getItem('count') if (savedCount) this.#count = parseInt(savedCount) this.#first = false }) } return this.#count } set count(v: number) { localStorage.setItem('count', v.toString()) this.#count = v } } ``` In reality, none of this is necessary โ€” you can make everything simpler by doing side-effects inside event handlers like `onclick` instead of using effects. In fact, we can just remove the effect and everything works: ```ts:counter.svelte.ts export class Counter { #first = true constructor(initial: number) { this.#count = $state(initial) } get count() { if (this.#first) { const savedCount = localStorage.getItem('count') if (savedCount) this.#count = parseInt(savedCount) this.#first = false } return this.#count } set count(v: number) { localStorage.setItem('count', v.toString()) this.#count = v } } ``` Unless you know what you're doing โ€” if you catch yourself using advanced runes like `$effect.root` or `$effect.tracking`, you're doing something wrong. ## How Svelte Reactivity Works I believe that understanding how something works gives you greater enjoyment in life by being more competent at what you do. I mentioned how Svelte uses signals for reactivity, but so do many other frameworks like Angular, Solid, Vue, and Qwik. There's even a [proposal to add signals to JavaScript](https://github.com/tc39/proposal-signals) itself. So far we learned that reassignments cause updates in Svelte. There's nothing special about `=` though! It just creates a function call to update the value: ```svelte:example {3} {value} ``` A signal is just a container that holds a value and subscribers that are notified when that value updates, so it doesn't do anything on its own: ```ts:signals.ts function state(value) { const signal = { value, subscribers: new Set() } return signal } ``` **You need effects to react to signals** and effects are just functions that run when a signal updates. That's how Svelte updates the DOM by compiling your template into effects. This is referred to as a **tracking context**: ```ts:example template_effect(() => set_text(text, get(value))) ``` Your entire app is a root effect with nested effects inside of it, so Svelte can keep track of your effects for cleanup. When the effect runs, it invokes the callback function and sets it as the active effect in some variable: ```ts:signals.ts let activeEffect = null function effect(fn) { // set active effect activeEffect = fn // run the effect fn() } ``` The magic happens when you read a signal inside of an effect. When `value` is read, it adds the active effect as a subscriber: ```ts:signals.ts // the active effect let activeEffect = fn function get(signal) { // add effect to subscribers signal.subscribers.add(activeEffect) // return value return signal.value } ``` Later, when you write to `count` it notifies the subscribers and recreates the dependency graph when it reads the signal inside the effect: ```ts:signals.ts function set(signal, value) { // update signal signal.value = value // notify subscribers signal.subscribers.forEach(effect => effect()) } ``` You can just update state and it's going to update the UI anywhere where it's used. If you're familiar with the observer pattern, signals are observables on steroids and frameworks like Svelte do a lot of work under the hood to make them performant. Here's a counter example using our basic signals implementation inside a regular `.html` file: ```html:example ``` This is oversimplified, but it happens every update and that's why it's called **runtime reactivity**, because it happens as your code runs! **Svelte doesn't compile reactivity**, it only compiles the implementation details. That's how you can use signals like a regular value. In other frameworks, you always have to read and write them using functions, or accessors. Deriveds are effects that track their own dependencies and return a signal โ€” you can pass a function with state inside to a derived, and it's tracked when it's read inside like an effect: ```svelte:example {7,14} {code} ``` I want to emphasize how `$state` is not some magic reactive container, but a regular value; which is why you need a function or a getter to get the latest value when the effect reruns โ€” unless you're using deep state. Here if `emoji.code` was a regular value and not a getter, the text inside the button would never update: ```svelte:example {5-6,15} ``` As the React people love to say, "it's just JavaScript!" ๐Ÿ˜„ ## Using Template Logic HTML doesn't have conditionals or loops, but Svelte has control flow blocks ranging from `{#if ...}`, `{#each ...}` to data loading blocks like `{#await ...}`. ### Using Conditionals In Svelte, you can use the `{#if ...}` block to conditionally render content: ```svelte:App.svelte {#if status === 'loading'}

Loading...

{:else if status === 'success'}

Success!

{:else if status === 'error'}

Error

{:else}

Impossible state

{/if} ``` ### Looping Over Data To loop over a list of items, you use the `{#each ...}` block: ```svelte:App.svelte
    {#each todos as todo}
  • {todo.text}
  • {:else}

    No items

    {/each}
``` You can [destructure](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) the value you're iterating over, get the current item index, and provide a unique key so Svelte can keep track of changes: ```svelte:App.svelte {2}
    {#each todos as { id, text, done }, i (id)}
  • {text}
  • {/each}
``` You can omit the `as` part inside the `{#each ...}` block when you just want to loop over an arbitrary amount of items. In this example, we're creating a basic grid: ```svelte:App.svelte
{#each Array(10), row} {#each Array(10), col}
{row},{col}
{/each} {/each}
``` You can loop over any iterable that works with `Array.from` from a `Map` and `Set` object, to generators: ```svelte:App.svelte
    {#each itemsMap as [key, value]}
  • {key}: {value}
  • {/each}
    {#each itemsSet as item}
  • {item}
  • {/each}
    {#each itemsGenerator() as item}
  • {item}
  • {/each}
``` Svelte even has reactive versions of built-in JavaScript objects, which we're going to look at later. ### Asynchronous Data Loading Previously, we fetched some pokemon data inside of an effect, but we haven't handled the loading, error, or success state. Svelte has an `{#await ...}` block for dealing with promises which handles loading, error, and success states: ```svelte:App.svelte {#await getPokemon('charizard')}

loading...

{:then pokemon}

{pokemon.name}

{pokemon.name} {:catch error}

{error.message}

{/await} ``` You can omit the `catch` block if you don't care about errors, and the initial block if you only care about the result: ```svelte:App.svelte {#await getPokemon('charizard') then pokemon}

{pokemon.name}

{pokemon.name} {/await} ``` ### Asynchronous Svelte Aside In the near future, you're going to be able to `await` a promise directly in a Svelte component. You can try it today by enabling the [experimental async flag](https://github.com/sveltejs/svelte/discussions/15845) in your Svelte config: ```ts:svelte.config.js export default { compilerOptions: { experimental: { async: true } } } ``` At the moment you have to create a [boundary](https://svelte.dev/docs/svelte/svelte-boundary) at the root of your app, or where you want to use the `await` keyword: ```svelte:App.svelte {#snippet pending()}

loading...

{/snippet} {@render children?.()}
``` Then inside of a component, you can use the `await` keyword in the script block, or template: ```svelte:Pokemon.svelte {5}

{pokemon.name}

{pokemon.name} ``` You can use the `$effect.pending` rune to show a loading state: ```svelte:Pokemon.svelte {#if $effect.pending()}

loading...

{:else}

{(await pokemon).name}

{(await {/if} ``` SvelteKit takes this even further with [remote functions](https://svelte.dev/docs/kit/remote-functions) where you can call remote functions like regular functions in the client, with type-safety across the server and client. ### Recreating Elements You can use the `{#key ...}` block to recreate elements when state updates. This is useful for replaying transitions, which we're going to learn about later: ```svelte:App.svelte {4,7-9} {#key value}
๐Ÿ‘ป
{/key} ``` ### Local Constants You can use the `@const` tag to define block-scoped readonly local constants in the Svelte template. Local constants can only be defined as a child of blocks like `{#if ...}`, `{#else ...}`, `{#await ...}`, and ``. In this example, we can destructure `text` and `done` from the `todo` object while keeping the original reference: ```svelte:App.svelte {3}
    {#each todos as todo} {@const { text, done: checked } = todo}
  • {text}
  • {/each}
``` In this example, we're creating a SVG grid using local constants to keep everything organized and legible: ```svelte:App.svelte {#each Array(tiles), col} {#each Array(tiles), row} {@const tile = size / tiles} {@const x = col * tile} {@const y = row * tile} {@const width = tile} {@const height = tile} {@const fill = (col + row) % 2 === 0 ? 'orangered' : 'white'} {/each} {/each} ``` ## Listening To Events You can listen to DOM events by adding attributes that start with `on` to elements. In the case of a mouse click, you would add the `onclick` attribute to a ` ``` You can spread events, since they're just attributes: ```svelte:App.svelte ``` This example uses the `onmousemove` event to update the mouse position: ```svelte:App.svelte
The mouse position is {mouse.x} x {mouse.y}
``` You can prevent the default behavior by using `e.preventDefault()`. This is useful for things like when you want to control a form with JavaScript and avoid a page reload: ```svelte:App.svelte {3}
``` ## Using Data Bindings In JavaScript, it's common to listen for the user input on the `` element through the `input` event, and update a value. This is called one-way data binding since updating the value doesn't update the input. In Svelte, you can use the `bind:` directive to keep them in sync. ### Two-Way Data Binding Having to do `value={search}` and `oninput={(e) => search = e.target.value}` on the `` element to update `search` is mundane for something you do often: ```svelte:App.svelte {3,4,10,11,15} search = (e.target as HTMLInputElement).value} />
    {#each filteredList as item}
  • {item}
  • {:else}

    No results

    {/each}
``` Thankfully, Svelte supports two-way data binding using the `bind:` directive. If you update the value, it updates the input and vice versa: ```svelte:App.svelte ``` One of the more useful bindings is `bind:this` to get a reference to a DOM node such as the `` element for example: ```svelte:App.svelte {3,10,14} ``` ### Function Bindings Another useful thing to know about are **function bindings** if you need to validate some input, or link one value to another. This example transforms the text the user types into the [Mocking SpongeBob](https://knowyourmeme.com/memes/mocking-spongebob) case: ```svelte:App.svelte ``` Instead of passing an expression like `bind:property={expression}`, you can pass a function binding like `bind:property={get, set}` to have more control over what happens when you read and write a value: ```svelte:App.svelte ``` ### Readonly Bindings Svelte provides two-way bindings, and readonly bindings for different elements you can find in the [Svelte documentation for bind](https://svelte.dev/docs/svelte/bind). There are media bindings for `