Learn

Svelte Tutorial

Svelte Tutorial

Based on the Svelte Tutorial at svelte.dev/tutorial. In case the main tutorial doesn’t provide a clear understanding of a concept, this document may provide additional explanations or examples.

Svelte is a modern JavaScript framework that is both performant and easy to learn. It is a compiler, not a library, which means that it generates highly optimized code that is small and fast. Svelte also has a clean and concise syntax that makes it easy to write maintainable code.

Introduction

What is Svelte?

Svelte is a tool for building fast web applications. Svelte converts your app into ideal JavaScript at build time, rather than interpreting your application code at run time.

<script>
	let name = 'world';
</script>

<h1>Hello world!</h1>

shorthand attribute: If the value name and the attribute name are the same, shorthand can be used, for example {src} is the same as src={src}

<img {src} alt="A man dances.">

nested components

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


<p>This is a paragraph.</p>
<Nested/>


<style>
 /* this style is scoped to this component */
	p {
		color: purple;
		font-family: 'Comic Sans MS', cursive;
		font-size: 2em;
	}
</style>

Reactivity

Assignments

Example event handler

<script>
	let count = 0;

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

<button on:click={incrementCount}>
	Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

Svelte instruments the assigment with code that tells the DOM it will need to be updated

/* App.svelte generated by Svelte v3.59.1 */
import {
	SvelteComponent,
	append,
	detach,
	element,
	init,
	insert,
	listen,
	noop,
	safe_not_equal,
	set_data,
	space,
	text
} from "svelte/internal";

function create_fragment(ctx) {
	let button;
	let t0;
	let t1;
	let t2;
	let t3_value = (/*count*/ ctx[0] === 1 ? 'time' : 'times') + "";
	let t3;
	let mounted;
	let dispose;

	return {
		c() {
			button = element("button");
			t0 = text("Clicked ");
			t1 = text(/*count*/ ctx[0]);
			t2 = space();
			t3 = text(t3_value);
		},
		m(target, anchor) {
			insert(target, button, anchor);
			append(button, t0);
			append(button, t1);
			append(button, t2);
			append(button, t3);

			if (!mounted) {
				dispose = listen(button, "click", /*incrementCount*/ ctx[1]);
				mounted = true;
			}
		},
		p(ctx, [dirty]) {
			if (dirty & /*count*/ 1) set_data(t1, /*count*/ ctx[0]);
			if (dirty & /*count*/ 1 && t3_value !== (t3_value = (/*count*/ ctx[0] === 1 ? 'time' : 'times') + "")) set_data(t3, t3_value);
		},
		i: noop,
		o: noop,
		d(detaching) {
			if (detaching) detach(button);
			mounted = false;
			dispose();
		}
	};
}

function instance($$self, $$props, $$invalidate) {
	let count = 0;

	function incrementCount() {
		$$invalidate(0, count += 1);
	}

	return [count, incrementCount];
}

class App extends SvelteComponent {
	constructor(options) {
		super();
		init(this, options, instance, create_fragment, safe_not_equal, {});
	}
}

export default App;

Declarations

Svelte’s reactivity keeps the DOM in sync with your application variables, but can also other variables in sync with other reactive delcarations.

let count = 0;
$: doubled = count * 2;

Usage:

<p>{count} doubled is {doubled}</p>

/* is same as */
<p>{count} doubled is {count*2}</p>

doubled is a reactive value. Reactive values are valuable when you need to reference it multiple times or when you have values that depend on other reactive values.

Statements

Statements can also be run reactively. For example, console.log whenever count changes

$: console.log('the count is ' + count);

Reactive statements can also be grouped within a block.

$: {
	console.log('the count is ' + count);
	alert('I SAID THE COUNT IS ' + count);
}

Can also be used with blocks like if

$: if (count >= 10) {
	alert('count is dangerously high!');
	count = 9;
}

Updating arrays and objects

Reactivity is triggered based on assignments. Methods that mutate arrays or objects will not trigger updates.

For example: addNumber won’t trigger re-calculation of sum

function addNumber() {
	numbers.push(numbers.length + 1);
}

$: sum = numbers.reduce((t, n) => t + n, 0);

The same issue exists for other array methods popshift, and splice and to object methods such as Map.setSet.add, etc. It also applies to indirect assignments to references:

const foo = obj.foo;
foo.bar = 'baz';

A workaround would be to assign numbers to itself; to tell the compiler that it changed

function addNumber() {
	numbers.push(numbers.length + 1);
	numbers = numbers;
}

Or via spread syntax

function addNumber() {
	numbers = [...numbers, numbers.length + 1];
}

Same applies for indirect reference, it should be followed by a assignment to the variable

const foo = obj.foo;
foo.bar = 'baz';
foo = foo

Rule of thumb: the updated variable must directly appear on the left hand side of the assignment.


Props

Declaring Props

Properties (generally shortened to props) are used to pass data from one component to its children.

Svelte uses export to declare properties

<script>
	export let answer;
</script>

<p>The answer is {answer}</p>

answer can be passed as a prop

<Nested answer={42}/>

Default values

Specified in the export

<script>
	export let answer = 'a mystery';
</script>
<Nested answer={42}/> 
<Nested/> <!-- value will be 'a mystery' -->

Spread props

You can pass an object’s properties and values as pros by spreading them

<Info {...pkg}/>

is the same as

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

	const pkg = {
		name: 'svelte',
		version: 3,
		speed: 'blazing',
		website: 'https://svelte.dev'
	};
</script>

<Info name={pkg.name} version={pkg.version} speed={pkg.speed} website={pkg.website}/>

Where Info has the props defined

<script>
	export let name;
	export let version;
	export let speed;
	export let website;
</script>

You can also access props not specified using export by accessing $$props directly. This is not recommended as it is difficult for Svelte to optimize.


Logic

Conditionals

If blocks

Svelte provides ways of expressing logic (like conditionals and loops) within html.

if block

{#if user.loggedIn}
	<button on:click={toggle}>
		Log out
	</button>
{/if}

Else blocks

Uses the : continuation tag for else or else if

{#if x > 10}
	<p>{x} is greater than 10</p>
{:else if 5 > x}
	<p>{x} is less than 5</p>
{:else}
	<p>{x} is between 5 and 10</p>
{/if}

Loops

Each block

each blocks can be used to loop over lists of data (any array or array-like object)

<ul>
	{#each cats as cat}
		<li><a href="/cat/{cat.id}">
			{cat.name}
		</a></li>
	{/each}
</ul>

Usage with destructuring

{#each cats as { id, name } }
	<li><a href="/cat/{id}">
		{i + 1}: {name}
	</a></li>
{/each}

Loop with index access

{#each cats as cat, i}
	<li><a href="/cat/{cat.id}">
		{i + 1}: {cat.name}
	</a></li>
{/each}

Keyed each blocks

Specifying unique identifiers for each element in the list is useful when updating values in the each block. It helps svelte figure out which DOM node to change when the component updates.

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

	let things = [
		{ id: 1, name: 'apple' },
		{ id: 2, name: 'banana' },
	];

	function handleClick() {
		things = things.slice(1);
	}
</script>

<button on:click={handleClick}>
	Remove first thing
</button>

{#each things as thing (thing.id) }
	<Thing name={thing.name}/>
{/each}

Await blocks

Svelte uses await blocks to deal with asynchronous data. await is used to await the value of promises directly.

{#await promise}
	<p>...waiting</p>
{:then number}
	<p>The number is {number}</p>
{:catch error}
	<p style="color: red">{error.message}</p>
{/await}
{#await promise then number}
	<p>the number is {number}</p>
{/await}

Events

DOM Events

on: directive

You can listen to any event on an element with on: directive

<div on:mousemove={handleMousemove}>
	The mouse position is {m.x} x {m.y}
</div>

Inline handlers

Event handlers can be declared inline

<div on:mousemove="{e => m = { x: e.clientX, y: e.clientY }}">
	The mouse position is {m.x} x {m.y}
</div>

Event modifiers

Event can take modifiers that can be used to alter the behavior of event handlers.

<script>
	function handleClick() {
		alert('this alert will only ever trigger once')
	}
</script>

<button on:click|once={handleClick}>
	Click me
</button>

Modifiers can be chained: on:click|once|capture={...}

Component Events

Components can dispatch custom events by using an event dispatcher.

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

	const dispatch = createEventDispatcher();

	function sayHello() {
		dispatch('message', {
			text: 'Hello!'
		});
	}
</script>

Parent components can use on: to listen for the custom event

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

	function handleMessage(event) {
		alert(event.detail.text);
	}
</script>

<Inner on:message={handleMessage}/>

Event Forwarding

Component events don’t bubble up, unlike DOM events. This means to listen to an event component levels deep each intermediate component would need to listen on it and manually forward (dispatch again).

How manual forwarding would look like (in the intermediate component)

<script>
	import Inner from './Inner.svelte';
	import { createEventDispatcher } from 'svelte';

	const dispatch = createEventDispatcher();

	function forward(event) {
		dispatch('message', event.detail);
	}
</script>

<Inner on:message={forward}/>

Svelte provides a short hand for event forwarding: using on: for the event without the event handler value. Meaning the code below would be the simpler equivalent to the one above.

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

<Inner on:message/>

DOM event forwarding

This same forwarding shorthand applies to DOM events, they can be forwarded to the parent using the shorthand. For example in order to handle the event in the parent.

child component

<button on:click>
	Click me
</button>

parent component

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

	function handleClick() {
		alert('child component button was clicked');
	}
</script>

<CustomButton on:click={handleClick}/>

Bindings

Text Inputs

Svelte data flow is top down - a parent component can set props to a child, and a component changes the attributes of an element, not vice versa.

The bind:value directive can be used to do the opposite, to allow an element change the value of a variable. This is cleaner than use of events to achieve similar behavior.

<script>
	let name = 'world';
</script>

<input bind:value={name}>

<h1>Hello {name}!</h1>

Numeric inputs

Svelte’s bind:value takes care of handling numeric inputs (no need to type cast from string to number before using).

Textarea inputs

<textarea bind:value={textValue}></textarea>

Checkbox inputs

Bind to checked attribute

<input type=checkbox bind:checked={yes}>

Group inputs

Multiple inputs relating to the same value can be grouped using the bind:group binding. For example: radio inputs or checkbox inputs

Radio input (one selection)

<label>
	<input type=radio bind:group={scoops} name="scoops" value={1}>
	One scoop
</label>

<label>
	<input type=radio bind:group={scoops} name="scoops" value={2}>
	Two scoops
</label>

<label>
	<input type=radio bind:group={scoops} name="scoops" value={3}>
	Three scoops
</label>

Checkbox input (multiple selection)

{#each menu as flavour}
	<label>
		<input type=checkbox bind:group={flavours} name="flavours" value={flavour}>
		{flavour}
	</label>
{/each}

handling

{#if flavours.length === 0}
	<p>Please select at least one flavour</p>
{:else if flavours.length > scoops}
	<p>Can't order more flavours than scoops!</p>
{:else}
	<p>
		You ordered {scoops} {scoops === 1 ? 'scoop' : 'scoops'}
		of {join(flavours)}
	</p>
{/if}

Select binding

bind:value can be used on select elements

<select bind:value={selected} on:change="{() => answer = ''}">
	{#each questions as question}
		<option value={question}>
			{question.text}
		</option>
	{/each}
</select>

Gotcha: If the binding has not yet initialized, selected will be uninitialized so cannot be used.

Workarounds:

  1. set an initial value for selected, or
  2. wait for selected to initialize before using the value, like below
<p>selected question {selected ? selected.id : '[waiting...]'}</p>

Select multiple

Select can use a multiple attribute to allow populate of an array rather than a single values

<h2>Flavours</h2>

<select multiple bind:value={flavours}>
	{#each menu as flavour}
		<option value={flavour}>
			{flavour}
		</option>
	{/each}
</select>

Contenteditable bindings

Elements with contenteditable support the bindings:

<div
	contenteditable="true"
	bind:innerHTML={html}
></div>

Each block binding

Properties inside #each blocks can be bound

{#each todos as todo}
	<div class:done={todo.done}>
		<input
			type=checkbox
			bind:checked={todo.done}
		>

		<input
			placeholder="What needs to be done?"
			bind:value={todo.text}
		>
	</div>
{/each}

Gotcha - Interacting with these inputs will mutate the array. Avoid these bindings if need immutable data, and use event handlers instead

Media elements

Bindings for <audio> and video elements:

Readonly bindings:

Two-way bindings:

Dimensions

Block-level elements have readonly bindings:

<div bind:clientWidth={w} bind:clientHeight={h}>
	<span style="font-size: {size}px">{text}</span>
</div>

This

this binding applies to every element and component

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

	let canvas;

	onMount(() => {
		const ctx = canvas.getContext('2d');
		ctx.fillStyle = "red";
		ctx.fillRect(10, 10, 100, 100);
	});
</script>

<canvas bind:this={canvas} width={120} height={120}></canvas>

Component Bindings

In Svelte you can bind to component props.

<Keypad bind:value={pin} on:submit={handleSubmit}/>

Binds pin to the value of the Keypad component

Gotcha: Use sparingly. It can be difficult to track flow of data with a lot of component bindings, especially without a ‘single source of truth’.

Binding to component instances

You can also bind to component instances (via this). This allows the parent component to interact with the component instance. Child component

<script>
	let input;

	export function focus() {
		input.focus();
	}
</script>

<input bind:this={input} />

Parent component

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

	let field;
</script>

<InputField bind:this={field}/>

<button on:click={() => field.focus()}>Focus field</button>

Lifecycle

onMount

Every component has a lifecycle that starts when it is created, and ends when it is destroyed. There are a handful of functions that allow you to run code at key moments during that lifecycle.

Lifecycle functions (aside from the tick function) must be called while the component is initializing , so that the callback is bound to the component instance.

Most lifecycle functions don’t run during Server-Side Rendering (SSR) apart from onDestroy.

onMount is the most commonly used lifecycle function:

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

	let photos = [];

	onMount(async () => {
		const res = await fetch(`/tutorial/api/album`);
		photos = await res.json();
	});
</script>

onDestroy

onDestroy lifecycle function enables you to run code when your component is destroyed.

This is useful in cleanup, for example cleaning up setInterval instance.

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

	let counter = 0;
	const interval = setInterval(() => counter += 1, 1000);

	onDestroy(() => clearInterval(interval));
</script>

Lifecycle functions can be called from a separate function (outside of the component) and imported in.

Example utils.js that uses onDestroy function

import { onDestroy } from 'svelte';

export function onInterval(callback, milliseconds) {
	const interval = setInterval(callback, milliseconds);

	onDestroy(() => {
		clearInterval(interval);
	});
}

Example components that calls function that uses a lifecycle function

<script>
	import { onInterval } from './utils.js';

	export let callback;
	export let interval = 1000;

	onInterval(callback, interval);
</script>

<p>
	This component executes a callback every 
	{interval} millisecond{interval === 1 ? '' : 's'}
</p>

<style>
	p { 
		border: 1px solid blue;
		padding: 5px;
	}
</style>

beforeUpdate and afterUpdate

beforeUpdate function schedules work to happen immediately before the DOM is updated. It’s used when you need to perform operations or computations using the current state right before the update.

afterUpdate function runs code once the DOM is in sync with data. It’s useful when you need to perform operations that require the updated state and DOM.

They are useful in doing things imperactively that are difficult to achieve in a purely state-driven way, like updating the scroll position of an element.

Example usage to enable autoscroll

<script>
  import { beforeUpdate, afterUpdate } from 'svelte';

  let weight = 70; // kg
  let height = 1.75; // m
  let bmi;

  beforeUpdate(() => {
    console.log(`Calculating BMI for weight ${weight} and height ${height}`);
  });

  afterUpdate(() => {
    console.log(`New BMI is ${bmi}`);
  });

  $: bmi = weight / (height * height);
</script>

<label>
  Weight (kg):
  <input type="number" bind:value={weight} />
</label>
<label>
  Height (m):
  <input type="number" bind:value={height} />
</label>

<p>Your BMI is: {bmi}</p>

Gotcha: afterUpdate runs after every state change and DOM update, not just the first one. This could lead to unnecessary code execution if not handled properly.

Workaround: To run code only after the first update, you could use a flag that checks if it’s the first update and, if not, prevents the code within afterUpdate from running.

tick

tick is a utility function that can be called at any time, not just when the component first initializes. It returns a promise that resolves as soon as any pending state changes have been applied to the DOM - or immediately, if there are no pending changes.

tick is similar to afterUpdate. However tick is an asynchronous utility function that returns a promise that resolves after the browser updates the DOM. afterUpdate is a synchronous lifecycle function that allows components to perform synchronous cleanup and logic immediately after an update occurs.

How Svelte updates work When you update component state, it doesn’t update the DOM immediately. It waits until the next microtask to see if there are any changes that need to be applied (including in other components). This avoids unnecessary work, and allows the browser to batch things more effectively.

tick is useful when you need to perform operations that require updated DOM state, such as reading updated element sizes, positions, or other properties that are affected by rendering.

Example

let message = "Initial Message";
let msgLength = 0;

async function updateMessage() {
  message = "Updated Message";
  await tick();
  msgLength = document.getElementById("message").offsetWidth;
}

Example breakdown:

A more practical usage example: to scroll to a newly added item

<script>
  import { tick } from 'svelte';
  
  let items = ['Item 1', 'Item 2'];

  async function addItem() {
    items = [...items, `Item ${items.length + 1}`];

    // Wait for the DOM to update
    await tick();

    const newItem = document.querySelector(`#item-${items.length}`);
    newItem.scrollIntoView({ behavior: 'smooth' });
  }
</script>

<ul>
  {#each items as item, i (i)}
    <li id="item-{i + 1}">{item}</li>
  {/each}
</ul>

<button on:click={addItem}>Add Item</button>

Gotcha: tick can introduce unnecessary complexity, inconsistent behavior and performance implications. Therefore, use sparingly, or even better, structure code in a way that avoids need for manual synchronization with DOM.


Stores

Writable Stores

A Svelte store is an object with a subscribe method that allows interested parties to be notified when the store value changes. This allows access by multiple unrelated component or regular javascript modules.

Example writable store store.js

import { writable } from 'svelte/store';

export const count = writable(0);

Resetter component resets the state

<script>
	import { count } from './stores.js';

	function reset() {
		count.set(0);
	}
</script>

<button on:click={reset}>
	reset
</button>

Update component increments and decrements

<script>
  import { count } from './stores.js';
  const increment = () => count.update(n => n + 1);
  // alternative syntax example of the decrement function
  function decrement() {
		count.update(n => n - 1);
  }
  const reset = () => count.set(0);
</script>

<button on:click={increment}>+</button>
<button on:click={decrement}>-</button>

<p>Count: {$count}</p>

main component subscribes to the value

<script>
	import { count } from './stores.js';
	import Updater from './Updater.svelte';
	import Resetter from './Resetter.svelte';

	let countValue;

	count.subscribe(value => {
		countValue = value;
	});
</script>

<h1>The count is {countValue}</h1>

<Updater/>
<Resetter/>

set vs update

Gotchas:

function addTask(newTask) {
  tasks.update(existingTasks => [...existingTasks, newTask]);
}

Auto-subscriptions

unsubscribe Subscribing to stores but not unsubscribing can lead to memory leaks if the component is instantiated and destroyed many times. The subscribe method returns an unsubscribe function that can be used for cleanup.

<script>
	import { onDestroy } from 'svelte';
	import { count } from './stores.js';
	
	let countValue;

	const unsubscribe = count.subscribe(value => {
		countValue = value;
	});

	onDestroy(unsubscribe);
</script>

<h1>The count is {countValue}</h1>

$ auto-subscription Svelte provides a shorthand to avoid this boilerplatey code. You can reference a store value by prefixing the store name with $. The code above can be replaced with:

<script>
	import { count } from './stores.js';
</script>

<h1>The count is {$count}</h1>

Readable Stores

Readable stores enable read-only access to store values. Not all values need to be editable, for example if capturing user geo-location or mouse coordinates.

A readable store is created using the readable function. This function accepts two arguments: an initial value and a start function. The start function is called when the first subscription to the store is made and should contain any logic required to produce and manage the value of the store.

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

  const time = readable(new Date(), function start(set) {
    const interval = setInterval(() => {
      set(new Date());
    }, 1000);

    return function stop() {
      clearInterval(interval);
    };
  });
</script>

<p>Current Time: {$time.toLocaleTimeString()}</p>

A more practical example illustrating use in polling for data

<script>
  import { readable } from 'svelte/store';
  import { getLatestData } from './api'; // imaginary API module

  const data = readable(null, (set) => {
    let intervalId;

    async function fetchData() {
      const latestData = await getLatestData();
      set(latestData);
    }

    // Start polling when the first subscriber starts listening
    fetchData();
    intervalId = setInterval(fetchData, 5000); // Poll every 5 seconds

    // Stop polling when the last subscriber unsubscribes
    return () => {
      clearInterval(intervalId);
    };
  });
</script>

<p>Latest data: {$data}</p>

Gotchas:

Derived stores

Derived stores are stores whose values are based on the value of one or more other stores.

export const elapsed = derived(
	time,
	$time => Math.round(($time - start) / 1000)
);

Another example

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

  const count = writable(0);
  const isEven = derived(count, $count => $count % 2 === 0);
</script>

<button on:click={() => count.update(n => n + 1)}>Increment</button>
<p>Count: {$count}</p>
<p>Is the count even? {$isEven}</p>

An example using multiple stores, set and a cleanup function

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

  const start = writable(false);
  const time = writable(new Date());

  // A store that updates every second, but only when "start" is true
  const timer = derived([start, time], ([$start, $time], set) => {
    let interval;
    if ($start) {
      interval = setInterval(() => {
        // set is useful for asynchronous updates
        // or anytime more than one store or reactive state is being checked
        // prevents unnecessary recalculation or stale data
        set(new Date());
      }, 1000);
    }

    return function cleanup() {
      clearInterval(interval);
    };
  });

</script>

<button on:click={() => $start = !$start}>Toggle Timer</button>
<p>Time: {$timer.toLocaleTimeString()}</p>

Gotchas:

Custom Stores

In Svelte, as long as an object implements the subscribe method, it is a store. This makes it easy to create custom stores with domain specific logic.

For example the initial count store could contain update logic within the store.

function createCount() {
	const { subscribe, set, update } = writable(0);

	return {
		subscribe,
		increment: () => update(n => n + 1),
		decrement: () => update(n => n - 1),
		reset: () => set(0)
	};
}

Practical Custom Store Examples A store that manages a list of items

<script>
  function createListStore() {
    const { subscribe, set, update } = writable([]);

    return {
      subscribe,
      add: item => update(list => [...list, item]),
      clear: () => set([])
    };
  }

  const list = createListStore();
</script>

<button on:click={() => list.add(Math.random())}>Add Random Number</button>
<button on:click={list.clear}>Clear</button>

<ul>
  {#each $list as item (item)}
    <li>{item}</li>
  {/each}
</ul>

A store that manages authentication

<script>
  function createAuthStore() {
    const { subscribe, set } = writable(null);

    return {
      subscribe,
      login: user => set(user),
      logout: () => set(null)
    };
  }

  const auth = createAuthStore();
</script>

<!-- Custom UI for application -->

Gotchas

Store bindings

If a store is writable — i.e. it has a set method — you can bind to its value, just as you can bind to local component state.

Example: store.js

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

export const name = writable('world');

export const greeting = derived(
	name,
	$name => `Hello ${$name}!`
);

component

<script>
	import { name, greeting } from './stores.js';
</script>

<h1>{$greeting}</h1>
<input bind:value={$name}>

<button on:click="{() => $name += '!'}">
	Add exclamation mark!
</button>

In this example $name += "!" is equivalent to name.set($name +"!")

Example binding to a custom store

<script>
  function createCounter() {
    const { subscribe, set, update } = writable(0);
    return {
      subscribe,
      increment: () => update(n => n + 1),
      reset: () => set(0)
    };
  }

  const count = createCounter();
</script>

<button on:click={count.increment}>Increment</button>
<button on:click={count.reset}>Reset</button>
<p>Count: {$count}</p>

Gotchas

double updates

<script>
  import { writable } from 'svelte/store';
  const username = writable('');
</script>

<input type="text" bind:value={$username} on:input={e => $username = e.target.value.toUpperCase()} />
<p>Username: {$username}</p>

In this example, when you type into the input field, the username store’s value is updated twice for each input event - once because of the two-way binding, and once because of the input event handler. This can cause issues and inefficiency, especially if your event handler is computationally expensive.

workaround

<script>
  import { writable } from 'svelte/store';
  const input = writable('');
  const username = derived(input, $input => $input.toUpperCase());
</script>

<input type="text" bind:value={$input} />
<p>Username: {$username}</p>

In this updated example, we avoid the issue by creating an input store that’s bound to the input field, and a username derived store that transforms input to uppercase. Now, when you type into the input field, the input store’s value is only updated once for each input event, and username is automatically updated in response. This makes the code more efficient and less prone to bugs.


Motion

Tweened

Svelte’s motion module provides a set of tools to smoothly transition between values over time. These tools include tweened and spring, which are store-based and reactive-friendly.

tweened function creates a store that smoothly transitions between values using a tweening function. It’s great for smooth, customizable animations.

Example: the paragraph element gradually fades out over two seconds when the button is clicked.

<script>
  import { tweened } from 'svelte/motion';
  let opacity = tweened(1, { duration: 2000 });
</script>

<button on:click={() => $opacity = 0}>Fade Out</button>
<p style="opacity: {$opacity}">Fade Me</p>

Example:

<script>
  import { tweened } from 'svelte/motion';
  import { cubicOut } from 'svelte/easing';
  const tween = tweened(0, { delay: 200, duration: 600, easing: cubicOut });
</script>

<button on:click={() => tween.set(Math.random() * 200)}>Move</button>
<div style="transform: translateX({$tween}px)">I'm moving</div>

Spring

spring Creates a store whose value transitions smoothly from one state to another using spring dynamics. You can control the spring’s behavior (springiness) using options such as stiffness and damping.

Example

<script>
  import { spring } from 'svelte/motion';
  const springValue = spring(0, { stiffness: 0.1, damping: 0.25 });
</script>

<button on:click={() => springValue.set(Math.random() * 200)}>Jump</button>
<div style="transform: translateY({$springValue}px)">I'm jumping</div>

Example illustrating how motion is affected by the option values

<script>
	import { spring } from 'svelte/motion';

	let coords = spring({ x: 50, y: 50 }, {
		stiffness: 0.1,
		damping: 0.25
	});

	let size = spring(10);
</script>

<div style="position: absolute; right: 1em;">
	<label>
		<h3>stiffness ({coords.stiffness})</h3>
		<input bind:value={coords.stiffness} type="range" min="0" max="1" step="0.01">
	</label>

	<label>
		<h3>damping ({coords.damping})</h3>
		<input bind:value={coords.damping} type="range" min="0" max="1" step="0.01">
	</label>
</div>

<svg
	on:mousemove="{e => coords.set({ x: e.clientX, y: e.clientY })}"
	on:mousedown="{() => size.set(30)}"
	on:mouseup="{() => size.set(10)}"
>
	<circle cx={$coords.x} cy={$coords.y} r={$size}/>
</svg>

<style>
	svg {
		width: 100%;
		height: 100%;
		margin: -8px;
	}
	circle {
		fill: #ff3e00;
	}
</style>

Gotchas


Transitions

The transition directive

We can make more appealing user interfaces by gracefully transitioning elements into and out of the DOM. Svelte makes this very easy with the transition directive.

The transition directive allows you to add an effect when an element is inserted or removed from the DOM. Import the desired effect function before use.

Example: fade function

<script>
	import { fade } from 'svelte/transition';
	let visible = true;
</script>

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

{#if visible}
	<p transition:fade>
		Fades in and out
	</p>
{/if}

Adding parameters

Transition functions often accept parameters for customizing the effect. Replace the fade transition with fly to show parameter use.

<script>
	import { fly } from 'svelte/transition';
	let visible = true;
</script>

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

{#if visible}
	<p transition:fly="{{ y: 200, duration: 2000 }}">
		Flies in and out
	</p>
{/if}

In and out

Instead of the transition directive, an element can have an in or an out directive, or both together.

The in and out directives apply transitions when an element enters and leaves the DOM respectively. These directives can’t be applied to elements that never enter or leave the DOM.

Example fly in and fade out

<script>
	import { fade, fly } from 'svelte/transition';
	let visible = true;
</script>

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

{#if visible}
	<p in:fly="{{ y: 200, duration: 2000 }}" out:fade>
		Flies in, fades out
	</p>
{/if}

Custom CSS transitions

The svelte/transition module has a handful of built-in transitions, but it’s very easy to create your own. By way of example, this is the source of the fade transition:

function fade(node, {
	delay = 0,
	duration = 400
}) {
	const o = +getComputedStyle(node).opacity;

	return {
		delay,
		duration,
		css: t => `opacity: ${t * o}`
	};
}

Example custom exaggerated spin function

<script>
	import { fade } from 'svelte/transition';
	import { elasticOut } from 'svelte/easing';

	let visible = true;

	function spin(node, { duration }) {
		return {
			duration,
			css: t => {
				const eased = elasticOut(t);

				return `
					transform: scale(${eased}) rotate(${eased * 1080}deg);
					color: hsl(
						${Math.trunc(t * 360)},
						${Math.min(100, 1000 - 1000 * t)}%,
						${Math.min(50, 500 - 500 * t)}%
					);`
			}
		};
	}
</script>

Remember: with great power comes great responsibility.

Custom JS transitions

While you should generally use CSS for transitions as much as possible, there are some effects that can’t be achieved without JavaScript, such as a typewriter effect:

function typewriter(node, { speed = 1 }) {
	const valid = (
		node.childNodes.length === 1 &&
		node.childNodes[0].nodeType === Node.TEXT_NODE
	);

	if (!valid) {
		throw new Error(`This transition only works on elements with a single text node child`);
	}

	const text = node.textContent;
	const duration = text.length / (speed * 0.01);

	return {
		duration,
		tick: t => {
			const i = Math.trunc(text.length * t);
			node.textContent = text.slice(0, i);
		}
	};
}


### Transition events
It can be useful to know when transitions are beginning and ending. Svelte dispatches events that you can listen to like any other DOM event:
```html
<p
	transition:fly="{{ y: 200, duration: 2000 }}"
	on:introstart="{() => status = 'intro started'}"
	on:outrostart="{() => status = 'outro started'}"
	on:introend="{() => status = 'intro ended'}"
	on:outroend="{() => status = 'outro ended'}"
>
	Flies in and out
</p>

Svelte fires introstart, introend, outrostart, and outroend events during transitions

Local transitions

Transitions will play on elements when any container block is added or destroyed. In the example here, toggling the visibility of the entire list also applies transitions to individual list elements.

Instead, we’d like transitions to play only when individual items are added and removed — in other words, when the user drags the slider.

We can achieve this with a local transition, which only plays when the block with the transition itself is added or removed:

<div transition:slide|local>
	{item}
</div>

Deferred transitions

With crossfade, you can create transitions that occur simultaneously. It makes use of a powerful feature of Svelte’s transition engine - the ability to defer transitions.

Crossfade can be used to pair transitions.

Example: using crossfade to pair send and receive transitions to move a todo list item to the opposite list - using motion to illustrate what’s happening

<script>
	import { quintOut } from 'svelte/easing';
	import { crossfade } from 'svelte/transition';

	const [send, receive] = crossfade({
		duration: d => Math.sqrt(d * 200),

		fallback(node, params) {
			const style = getComputedStyle(node);
			const transform = style.transform === 'none' ? '' : style.transform;

			return {
				duration: 600,
				easing: quintOut,
				css: t => `
					transform: ${transform} scale(${t});
					opacity: ${t}
				`
			};
		}
	});

	let uid = 1;

	let todos = [
		{ id: uid++, done: false, description: 'write some docs' },
		{ id: uid++, done: false, description: 'start writing blog post' },
		{ id: uid++, done: true,  description: 'buy some milk' },
		{ id: uid++, done: false, description: 'mow the lawn' },
		{ id: uid++, done: false, description: 'feed the turtle' },
		{ id: uid++, done: false, description: 'fix some bugs' },
	];

	function add(input) {
		const todo = {
			id: uid++,
			done: false,
			description: input.value
		};

		todos = [todo, ...todos];
		input.value = '';
	}

	function remove(todo) {
		todos = todos.filter(t => t !== todo);
	}

	function mark(todo, done) {
		todo.done = done;
		remove(todo);
		todos = todos.concat(todo);
	}
</script>

<div class='board'>
	<input
		placeholder="what needs to be done?"
		on:keydown={e => e.key === 'Enter' && add(e.target)}
	>

	<div class='left'>
		<h2>todo</h2>
		{#each todos.filter(t => !t.done) as todo (todo.id)}
			<label
				in:receive="{{key: todo.id}}"
				out:send="{{key: todo.id}}"
			>
				<input type=checkbox on:change={() => mark(todo, true)}>
				{todo.description}
				<button on:click="{() => remove(todo)}">remove</button>
			</label>
		{/each}
	</div>

	<div class='right'>
		<h2>done</h2>
		{#each todos.filter(t => t.done) as todo (todo.id)}
			<label
				class="done"
				in:receive="{{key: todo.id}}"
				out:send="{{key: todo.id}}"
			>
				<input type=checkbox checked on:change={() => mark(todo, false)}>
				{todo.description}
				<button on:click="{() => remove(todo)}">remove</button>
			</label>
		{/each}
	</div>
</div>

<style>
	.board {
		display: grid;
		grid-template-columns: 1fr 1fr;
		grid-gap: 1em;
		max-width: 36em;
		margin: 0 auto;
	}

	.board > input {
		font-size: 1.4em;
		grid-column: 1/3;
	}

	h2 {
		font-size: 2em;
		font-weight: 200;
		user-select: none;
		margin: 0 0 0.5em 0;
	}

	label {
		position: relative;
		line-height: 1.2;
		padding: 0.5em 2.5em 0.5em 2em;
		margin: 0 0 0.5em 0;
		border-radius: 2px;
		user-select: none;
		border: 1px solid hsl(240, 8%, 70%);
		background-color:hsl(240, 8%, 93%);
		color: #333;
	}

	input[type="checkbox"] {
		position: absolute;
		left: 0.5em;
		top: 0.6em;
		margin: 0;
	}

	.done {
		border: 1px solid hsl(240, 8%, 90%);
		background-color:hsl(240, 8%, 98%);
	}

	button {
		position: absolute;
		top: 0;
		right: 0.2em;
		width: 2em;
		height: 100%;
		background: no-repeat 50% 50% url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23676778' d='M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M17,7H14.5L13.5,6H10.5L9.5,7H7V9H17V7M9,18H15A1,1 0 0,0 16,17V10H8V17A1,1 0 0,0 9,18Z'%3E%3C/path%3E%3C/svg%3E");
		background-size: 1.4em 1.4em;
		border: none;
		opacity: 0;
		transition: opacity 0.2s;
		text-indent: -9999px;
		cursor: pointer;
	}

	label:hover button {
		opacity: 1;
	}
</style>

Key blocks

Key blocks destroy and recreate their contents when the value of an expression changes.

{#key value}
	<div transition:fade>{value}</div>
{/key}

Key blocks use the each block and a key expression to transition between lists of elements. Elements with the same key in sequential lists won’t transition.

This is useful if you want an element to play its transition whenever a value changes instead of only when the element enters or leaves the DOM.

Wrap the <span> element in a key block depending on number. This will make the animation play whenever you press the increment button.


Animations

animate

In the previous chapter, we used deferred transitions to create the illusion of motion as elements move from one todo list to the other.

To complete the illusion, we also need to apply motion to the elements that aren’t transitioning. For this, we use the animate directive.

First, import the flip function — flip stands for ‘First, Last, Invert, Play’ — from svelte/animate:

Example that shows motion being applied to elements that are not being transitioned

<script>
	import { quintOut } from 'svelte/easing';
	import { crossfade } from 'svelte/transition';
	import { flip } from 'svelte/animate';

	const [send, receive] = crossfade({
		duration: d => Math.sqrt(d * 200),

		fallback(node, params) {
			const style = getComputedStyle(node);
			const transform = style.transform === 'none' ? '' : style.transform;

			return {
				duration: 600,
				easing: quintOut,
				css: t => `
					transform: ${transform} scale(${t});
					opacity: ${t}
				`
			};
		}
	});

	let uid = 1;

	let todos = [
		{ id: uid++, done: false, description: 'write some docs' },
		{ id: uid++, done: false, description: 'start writing blog post' },
		{ id: uid++, done: true,  description: 'buy some milk' },
		{ id: uid++, done: false, description: 'mow the lawn' },
		{ id: uid++, done: false, description: 'feed the turtle' },
		{ id: uid++, done: false, description: 'fix some bugs' },
	];

	function add(input) {
		const todo = {
			id: uid++,
			done: false,
			description: input.value
		};

		todos = [todo, ...todos];
		input.value = '';
	}

	function remove(todo) {
		todos = todos.filter(t => t !== todo);
	}

	function mark(todo, done) {
		todo.done = done;
		remove(todo);
		todos = todos.concat(todo);
	}
</script>

<div class='board'>
	<input
		placeholder="what needs to be done?"
		on:keydown={e => e.key === 'Enter' && add(e.target)}
	>

	<div class='left'>
		<h2>todo</h2>
		{#each todos.filter(t => !t.done) as todo (todo.id)}
			<label
				in:receive="{{key: todo.id}}"
				out:send="{{key: todo.id}}"
				animate:flip
			>
				<input type=checkbox on:change={() => mark(todo, true)}>
				{todo.description}
				<button on:click="{() => remove(todo)}">remove</button>
			</label>
		{/each}
	</div>

	<div class='right'>
		<h2>done</h2>
		{#each todos.filter(t => t.done) as todo (todo.id)}
			<label
				class="done"
				in:receive="{{key: todo.id}}"
				out:send="{{key: todo.id}}"
				animate:flip
			>
				<input type=checkbox checked on:change={() => mark(todo, false)}>
				{todo.description}
				<button on:click="{() => remove(todo)}">remove</button>
			</label>
		{/each}
	</div>
</div>

<style>
	.board {
		display: grid;
		grid-template-columns: 1fr 1fr;
		grid-gap: 1em;
		max-width: 36em;
		margin: 0 auto;
	}

	.board > input {
		font-size: 1.4em;
		grid-column: 1/3;
	}

	h2 {
		font-size: 2em;
		font-weight: 200;
		user-select: none;
		margin: 0 0 0.5em 0;
	}

	label {
		position: relative;
		line-height: 1.2;
		padding: 0.5em 2.5em 0.5em 2em;
		margin: 0 0 0.5em 0;
		border-radius: 2px;
		user-select: none;
		border: 1px solid hsl(240, 8%, 70%);
		background-color:hsl(240, 8%, 93%);
		color: #333;
	}

	input[type="checkbox"] {
		position: absolute;
		left: 0.5em;
		top: 0.6em;
		margin: 0;
	}

	.done {
		border: 1px solid hsl(240, 8%, 90%);
		background-color:hsl(240, 8%, 98%);
	}

	button {
		position: absolute;
		top: 0;
		right: 0.2em;
		width: 2em;
		height: 100%;
		background: no-repeat 50% 50% url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23676778' d='M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M17,7H14.5L13.5,6H10.5L9.5,7H7V9H17V7M9,18H15A1,1 0 0,0 16,17V10H8V17A1,1 0 0,0 9,18Z'%3E%3C/path%3E%3C/svg%3E");
		background-size: 1.4em 1.4em;
		border: none;
		opacity: 0;
		transition: opacity 0.2s;
		text-indent: -9999px;
		cursor: pointer;
	}

	label:hover button {
		opacity: 1;
	}
</style>

Actions

use Directive

Actions are essentially element-level lifecycle functions. They’re useful for things like:

Actions are functions that are invoked when an element is created. They provide a way to apply reusable behavior to elements, such as adding event listeners or applying third-party libraries. You might use actions to interact with non-Svelte libraries, apply DRY principles, or add complex interactivity to an element.

An action can be useful in many scenarios. For instance, when you want to integrate a third-party library that manipulates the DOM directly, you can use an action to initialize the library’s code when an element is created, and clean it up when the element is destroyed.

Example: an action that focus an input element when it’s used

<script>
  function autofocus(node) {
    node.focus();
  }
</script>

<input use:autofocus />

Example: close modal when user clicks outside it

<script>
    // this function can reside in a separate utility js file
	function clickOutside(node) {
		const handleClick = (event) => {
			if (!node.contains(event.target)) {
				node.dispatchEvent(new CustomEvent("outclick"));
			}
		};
	
		document.addEventListener("click", handleClick, true);
	
		return {
			destroy() {
				document.removeEventListener("click", handleClick, true);
			}
		};
	}

	let showModal = true;
</script>

<button on:click={() => (showModal = true)}>Show Modal</button>
{#if showModal}
	<div class="box" use:clickOutside on:outclick={() => (showModal = false)}>
		Click outside me!
	</div>
{/if}

<style>
	.box {
		--width: 100px;
		--height: 100px;
		position: absolute;
		width: var(--width);
		height: var(--height);
		left: calc(50% - var(--width) / 2);
		top: calc(50% - var(--height) / 2);
		display: flex;
		align-items: center;
		padding: 8px;
		border-radius: 4px;
		background-color: #ff3e00;
		color: #fff;
		text-align: center;
		font-weight: bold;
	}
</style>

Adding parameters to actions

An action can take an argument, which the action function will be called with alongside the element it belongs to. These arguments can help customize the actions behavior.

Example: emit event when a user holds press on a button

<script>
    // this function can reside in a separate utility js file
	function longpress(node, duration) {
		let timer;
		
		const handleMousedown = () => {
			timer = setTimeout(() => {
				node.dispatchEvent(
					new CustomEvent('longpress')
				);
			}, duration);
		};
		
		const handleMouseup = () => {
			clearTimeout(timer)
		};
	
		node.addEventListener('mousedown', handleMousedown);
		node.addEventListener('mouseup', handleMouseup);
	
		return {
			update(newDuration) {
				duration = newDuration;
			},
			destroy() {
				clearTimeout(timer);
				node.removeEventListener('mousedown', handleMousedown);
				node.removeEventListener('mouseup', handleMouseup);
			}
		};
	}

	let pressed = false;
	let duration = 2000;
</script>

<label>
	<input type=range bind:value={duration} max={2000} step={100}>
	{duration}ms
</label>

<button use:longpress={duration}
	on:longpress="{() => pressed = true}"
	on:mouseenter="{() => pressed = false}"
>press and hold</button>

{#if pressed}
	<p>congratulations, you pressed and held for {duration}ms</p>
{/if}

Example: Action that adds a custom tooltip on an element

<script>
  function tooltip(node, text) {
    const toolTipElement = document.createElement('div');
    toolTipElement.textContent = text;
    document.body.appendChild(toolTipElement);
    node.addEventListener('mouseover', () => toolTipElement.style.visibility = 'visible');
    node.addEventListener('mouseout', () => toolTipElement.style.visibility = 'hidden');

    return {
      destroy() {
        document.body.removeChild(toolTipElement);
      }
    };
  }
</script>

<button use:tooltip={'Click me!'}>Hover over me</button>

Gotcha: Actions can only be used on DOM elements.


Advanced Styling

class Directive

With Svelte, class attributes can be used directly with JavasSript

<div
	class="{current === 'foo' ? 'selected' : ''}"
>foo</div>

With Svelte, you can use class names as special directives that will add the class if the value of the expression is truthy. The code below is the same as the one above.

<div
	class:selected="{current === 'foo'}"
>foo</div>

Class Shorthand

Often, the name of the class will be the same as the name of the value it depends on:

<div class:big={big}>
	<!-- ... -->
</div>

In those cases the shorthand form can be used

<div class:big>
	<!-- ... -->
</div>

Inline Styles

Apart from adding styles inside style tags, you can also add styles to individual elements using the style attribute. Usually you will want to do styling through CSS, but this can come in handy for dynamic styles, especially when combined with CSS custom properties.

Add the following style attribute to the paragraph element: style="color: {color}; --opacity: {bgOpacity};"

Great, now you can style the paragraph using variables that change based on your input without having to make a class for every possible value.

Style Directive

Svelte provides a nicer way to write inline styles with the style directive. This helps avoid unwieldy inline css syntax.

Change the style attribute of the paragraph to the following:

<p 
	style:color 
	style:--opacity="{bgOpacity}"
>

Like the class directive, the shorthand can be applied when the name of the property and the variable are the same:

Similar to the class directive, the style directive will take precedence if you try to set the same property through a style attribute.


Component Composition

Slots

Components, just like other HTML elements can have children. For example

<div>
	<p>I'm a child of div</p>
</div>

<Box>
	<p>I'm a child of Box</p>
</Box>

The component uses <slot> elements to determine where to place these child components. For example, if this is the component content:

<div id="box">
	<slot></slot>
</div>

If children are added to the component:

<Box>
	<h1>child 1: header</h1>
	<p>child 2: paragraph</p>
</Box>

The children be placed in the <slot> location in the component.

Slot fallbacks

A component can specify fallbacks for any slots that are left empty, by putting content inside the <slot> element:

<div class="box">
	<slot>
		<em>no content was provided</em>
	</slot>
</div>

We can now create instances of <Box> without any children:

<Box>
	<h2>Hello!</h2>
	<p>This is a box. It can contain anything.</p>
</Box>

<Box/>

Named slots

The previous example contained a default slot, which renders the direct children of a component. Sometimes you will need more control over placement, such as with this <ContactCard>. In those cases, we can use named slots.

In ContactCard.svelte, add a name attribute to each slot:

<article class="contact-card">
	<h2>
		<slot name="name">
			<span class="missing">Unknown name</span>
		</slot>
	</h2>

	<div class="address">
		<slot name="address">
			<span class="missing">Unknown address</span>
		</slot>
	</div>

	<div class="email">
		<slot name="email">
			<span class="missing">Unknown email</span>
		</slot>
	</div>
</article>

Then, add elements with corresponding slot="..." attributes inside the <ContactCard> component:

<ContactCard>
	<span slot="name">
		P. Sherman
	</span>

	<span slot="address">
		42 Wallaby Way<br>
		Sydney
	</span>
</ContactCard>

Optional slots

In some cases, you may want to control parts of your component based on whether the parent passes in content for a certain slot. Perhaps you have a wrapper around that slot, and you don’t want to render it if the slot is empty. Or perhaps you’d like to apply a class only if the slot is present. You can do this by checking the properties of the special $$slots variable.

$$slots is an object whose keys are the names of the slots passed in by the parent component. If the parent leaves a slot empty, then $$slots will not have an entry for that slot.

Notice that both instances of <Project> in this example render a container for comments and a notification dot, even though only one has comments. We want to use $$slots to make sure we only render these elements when the parent <App> passes in content for the comments slot.

In Project.svelte, update the class:has-discussion directive on the <article>:

<article class:has-discussion={$$slots.comments}>

Next, wrap the comments slot and its wrapping <div> in an if block that checks $$slots:

{#if $$slots.comments}
	<div class="discussion">
		<h3>Comments</h3>
		<slot name="comments"></slot>
	</div>
{/if}

Now the comments container and the notification dot won’t render when <App> leaves the comments slot empty.

Slot props

In this app, we have a <Hoverable> component that tracks whether the mouse is currently over it. It needs to pass that data back to the parent component, so that we can update the slotted contents.

For this, we use slot props. In Hoverable.svelte, pass the hovering value into the slot:

<div on:mouseenter={enter} on:mouseleave={leave}>
	<slot hovering={hovering}></slot>
</div>

Remember you can also use the {hovering} shorthand, if you prefer.

Then, to expose hovering to the contents of the <Hoverable> component, we use the let directive:

<Hoverable let:hovering={hovering}>
	<div class:active={hovering}>
		{#if hovering}
			<p>I am being hovered upon.</p>
		{:else}
			<p>Hover over me!</p>
		{/if}
	</div>
</Hoverable>

You can rename the variable, if you want — let’s call it active in the parent component:

<Hoverable let:hovering={active}>
	<div class:active>
		{#if active}
			<p>I am being hovered upon.</p>
		{:else}
			<p>Hover over me!</p>
		{/if}
	</div>
</Hoverable>

You can have as many of these components as you like, and the slotted props will remain local to the component where they’re declared.

Named slots can also have props; use the let directive on an element with a slot="..." attribute, instead of on the component itself.


Context API

Context API

Svelte’s context API allows components to share data without passing it through props. This is especially useful when deeply nested component need aces to shared state e.g. user authentication.

It makes use of 2 lifecycle functions: setContext and getContext

Like most other lifecycle functions, these must be initialized in the top level script tag (not inside reactive statements).

Example: Use context for a Theme switcher Parent component

<script>
  import { setContext } from 'svelte';
  let key = {};
  let theme = 'light';
  setContext(key, { theme });
</script>

<button on:click={() => theme = theme === 'light' ? 'dark' : 'light'}>Switch theme</button>
<Child />

Child component

<script>
  import { getContext } from 'svelte';
  let key = {};
  let { theme } = getContext(key);
</script>

<p>Current theme is {theme}</p>

Context vs Stores

While both can be used for state management, context is limited to component hierarchies / component trees (only available to a component and its descendants), while stores are globally accessible.

Another common usage for context is for authentication / sharing user data on the app.

<script>
  import { setContext } from 'svelte';
  let key = {};
  let user = { id: 1, name: 'John' };
  setContext(key, { user });
</script>

<Profile />

In Profile, or other components:

<script>
  import { getContext } from 'svelte';
  let key = {};
  let { user } = getContext(key);
</script>

<p>Welcome, {user.name}!</p>

Context Keys

Context keys identify data being shared via the Context API. Keys can be any JavaScript object or primitive, but it is recommended to use a private key: an object that’s not exported from your module, to prevent collisions. For example, a string value for a key could be used by other third party libraries.


Special Elements

svelte self

This special element enables a component to include itself in its markup. It’s typically used for recursive components, such as a navigation tree.

Example: In the below example, if an item has children, it will then “import itself” with each of the children items.

<script>
  let items = [
    { id: 1, children: [{ id: 2 }] },
    { id: 3, children: [] }
  ];
</script>

<ul>
  {#each items as item (item.id)}
    <li>
      Item {item.id}
      {#if item.children.length}
        <svelte:self {item.children} />
      {/if}
    </li>
  {/each}
</ul>

svelte component

This element dynamically includes a component. It’s useful when the component type can change during the application’s lifecycle.

Example

<script>
  import ComponentA from './ComponentA.svelte';
  import ComponentB from './ComponentB.svelte';

  let Component = ComponentA;
</script>

<button on:click={() => Component = Component === ComponentA ? ComponentB : ComponentA}>
  Toggle Component
</button>

<svelte:component this={Component} />

svelte element

<svelte:element> is used to create elements dynamically. The element type can be bound to a variable and changed at runtime.

Example

<script>
  let ElementType = 'button'; // ensure this is a valid HTML tag
</script>

<svelte:element this={ElementType} on:click={() => alert('Element clicked!')}>
  Click me!
</svelte:element>

<button on:click={() => ElementType = ElementType === 'button' ? 'div' : 'button'}>
  Toggle Element Type
</button>

svelte window, svelte document, svelte body

These elements <svelte:window>, <svelte:document>, <svelte:body> allow you to add event listeners on the corresponding DOM elements: window, document,body

svelte:window

<svelte:window on:keydown={handleKeydown}/>

We can also bind to certain properties of window:

<svelte:window bind:scrollY={y}/>

The list of properties you can bind to is as follows:

svelte:document

<svelte:document on:selectionchange={handleSelectionChange} />

This is useful with events like selectionchange, which doesn’t fire on window.

svelte:body

<svelte:body
	on:mouseenter={handleMouseenter}
	on:mouseleave={handleMouseleave}
/>

This is useful with the mouseenter and mouseleave events, which don’t fire on window.

svelte head

This element lets you manipulate the document’s <head>. It’s useful for changing the title or adding meta tags.

<svelte:head>
  <title>New Title</title>
  <meta name="description" content="New Description" />
</svelte:head>

Gotcha: Changes only apply while the component is mounted, and are undone when the component is destroyed. If you need persistent changes, consider using a library or server-side rendering.

svelte options

This element lets you specify compile-time component options.

<svelte:options immutable={true} />

svelte fragments

<svelte:fragment> is used when you want to group multiple elements together without adding extra nodes to the DOM (like you would with a div or span). This is similar to React’s <> (React.Fragment) component.

Example

<footer>
    <p>Footer here</p>
	<svelte:fragment slot="footer">		
		<p>Copyright (c) 2019 Svelte Industries</p>
		<p>All rights reserved.</p>
	</svelte:fragment>
</footer>

will render as

<footer>
    <p>Footer here</p>
	<p>Copyright (c) 2019 Svelte Industries</p>
	<p>All rights reserved.</p>
</footer>

Module Context

Sharing Code

In a Svelte component, you can use the context="module" directive to specify scripts that run once per component, rather than once per component instance. This can be used for defining shared code, as well as creating component-level exports.

Example

<script context="module">
  let count = 0;

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

<script>
  import { increment } from './Counter.svelte';
  
  function handleClick() {
    increment();
  }
</script>

<button on:click={handleClick}>
  Click me!
</button>

Using Module Context to Share Code: With context="module", you can define functions, variables, or imports that you want to be shared among all instances of a component.

<script context="module">
  let sharedData = fetch('/api/data').then(res => res.json());
</script>

<script>
  let data;

  async function loadData() {
    data = await sharedData;
  }
</script>

<button on:click={loadData}>
  Load data
</button>

<p>{data}</p>

Module Exports

You can also define exports at the component level, which are useful when you want to expose a component’s API. These can be functions, variables, or even Svelte stores.

<script context="module">
  import { writable } from 'svelte/store';

  export const count = writable(0);

  export function increment() {
    count.update(n => n + 1);
  }
</script>

<script>
  import { increment, count } from './Counter.svelte';
  
  function handleClick() {
    increment();
  }
</script>

<button on:click={handleClick}>
  Click me!
</button>

<p>{$count}</p>

Special tags

Debug

The @debug tag is a developer tool that allows you to pause execution and inspect the current state of your application at any point. You can add any variable you want to observe as an argument.

<script>
  let variable1 = 'hello';
  let variable2 = 'world';
</script>

{@debug variable1, variable2}

The @debug tag is removed in the production build, so don’t rely on it for any functionality that should exist in the final app.

HTML Tags

The @html tag allows you to insert HTML directly into your components. Be careful, though: if you’re inserting user-generated content, it’s a potential security risk.

Example:

<script>
  let htmlContent = '<p>This is a <strong>strong</strong> statement.</p>';
</script>

{@html htmlContent}

Gotcha: Svelte does not sanitize HTML content. Any HTML you insert via @html is inserted as is, which can open your app up to Cross-Site Scripting (XSS) attacks if the content comes from an untrusted source.


Next Steps

You’ve now finished the Svelte tutorial and are ready to start building apps. You can refer back to individual chapters at any time (click the title above to reveal a dropdown) or continue your learning via the API referenceExamples and Blog. If you’re a Twitter user, you can get updates via @sveltejs.

To get set up in your local development environment, check out the quickstart guide.

If you’re looking for a more expansive framework that includes routing, server-side rendering and everything else, take a look at SvelteKit.

SvelteKit Tutorial