Tobias Davis

Designing better software systems.

Articles | Contact | Newsletter | RSS

Site logo image

Because I still use Svelte 1 in some projects 😱 and the docs are currently only available through the Internet Archive, I made a backup copy here and tidied it up a bit.

Table of Contents #

Introduction #

What is Svelte? #

If you’ve ever built a JavaScript application, the chances are you’ve encountered – or at least heard of – frameworks like React, Angular, Vue and Ractive. Like Svelte, these tools all share a goal of making it easy to build slick interactive user interfaces.

But Svelte has a crucial difference: rather than interpreting your application code at run time, your app is converted into ideal JavaScript at build time. That means you don’t pay the performance cost of the framework’s abstractions, or incur a penalty when your app first loads.

And because there’s no overhead, you can easily adopt Svelte in an existing app incrementally, or ship widgets as standalone packages that work anywhere.

Understanding Svelte components #

In Svelte, an application is composed from one or more components. A component is a reusable self-contained block of code that encapsulates markup, styles and behaviours that belong together.

Like Ractive and Vue, Svelte promotes the concept of single-file components: a component is just an .html file. Here’s a simple example:

<!-- App.html -->
<h1>Hello !</h1>

Svelte turns this into a JavaScript module that you can import into your app:

// main.js
import App from './App.html';

const app = new App({
target: document.querySelector( 'main' ),
data: { name: 'world' }
});

// change the data associated with the template
app.set({ name: 'everybody' });

// detach the component and clean everything up
app.teardown();

Congratulations, you’ve just learned about half of Svelte’s API!

Getting started #

Normally, this is the part where the instructions might tell you to add the framework to your page as a <script> tag. But because Svelte runs at build time, it works a little bit differently.

The best way to use Svelte is to integrate it into your build system – there are plugins for Rollup, Browserify, Gulp and others, with more on the way. See here for an up-to-date list.

Right now, for the purposes of demonstration, we’ll use svelte-cli the command line interface.

ℹ️ You will need to have Node.js installed, and have some familiarity with the command line.

First, install the CLI:

npm install -g svelte-cli

Then, create a directory for the project:

mkdir my-svelte-project
cd my-svelte-project

Inside my-svelte-project, create a HelloWorld.html file with the following contents:

<h1>Hello </h1>

Compile it:

svelte compile --format iife HelloWorld.html > HelloWorld.js

The --format iife bit means ‘generate an immediately-invoked function expression’ – this allows us to use the component as a simple <script> tag. (By default, Svelte will create a JavaScript module instead, which is recommended for more serious applications but requires additional steps.)

Create an index.html page and include the script we just generated:

<!doctype html>
<html>
<head>
<title>My first Svelte app</title>
</head>
<body>
<main></main>
<script src='HelloWorld.js'></script>
<script>
var app = new HelloWorld({
target: document.querySelector( 'main' ),
data: {
name: 'world'
}
});
</script>
</body>
</html>

Finally, open the page in your browser – open index.html – and interact with app via the console using the API.

Component API #

As we saw above, you create a component instance with the new keyword:

import MyComponent from './MyComponent.html';

const component = new MyComponent({
// `target` is the only required option – the element
// to render the component to
target: document.querySelector( 'main' ),

// `data` is optional. A component can also have
// default data – we'll learn about that later.
data: {
questions: [
'life',
'the universe',
'everything'
],
answer: 42
}
});

Every Svelte component instance has a small number of methods you can use to control it, in addition to any custom methods you add.

component.set(data) #

This updates the component’s state with the new values provided and causes the DOM to update. data must be a plain old JavaScript object (POJO). Any properties not included in data will remain as they were.

component.set({
questions: [
'why is the sky blue?',
'how do planes fly?',
'where do babies come from?'
],
answer: 'ask your mother'
});

ℹ️ If you’ve used Ractive in the past, this is very similar to ractive.set(...), except that instead of doing ractive.set('foo', 'bar') you must always do ractive.set({foo: 'bar'}), and you cannot set nested keypaths directly. It’s also very similar to React’s setState, except that it causes synchronous updates, meaning the DOM is always in a predictable state.

component.get(key) #

Returns the current value of key:

console.log( component.get( 'answer' ) ); // 'ask your mother'

This will also retrieve the value of computed properties.

component.observe(key, callback[, options]) #

This method allows you to respond to changes in state, which is particularly useful when combined with lifecycle hooks and two-way bindings.

const observer = component.observe( 'answer', answer => {
console.log( `the answer is ${answer}` );
});
// fires immediately with current answer:
// -> 'the answer is ask your mother'

component.set({ answer: 'google it' });
// -> 'the answer is google it'

observer.cancel(); // further changes will be ignored

The callback takes two arguments – the current value and the previous value. (The first time it is called, the second argument will be undefined):

thermometer.observe( 'temperature', ( newValue, oldValue ) => {
if ( oldValue === undefined ) return;
console.log( `it's getting ${newValue > oldValue ? 'warmer' : 'colder'}` );
});

If you don’t want the callback to fire when you first attach the observer, use init: false:

thermometer.observe( 'temperature', ( newValue, oldValue ) => {
console.log( `it's getting ${newValue > oldValue ? 'warmer' : 'colder'}` );
}, { init: false });

ℹ️ For primitive values like strings and numbers, observer callbacks are only called when the value changes. But because it’s possible to mutate an object or array while preserving referential equality, Svelte will err on the side of caution. In other words, if you do component.set({foo: component.get('foo')}), and foo is an object or array, any foo observers will be triggered.

By default, observers are called before the DOM updates, giving you a chance to perform any additional updates without touching the DOM more than is necessary. In some cases – for example, if you need to measure an element after the DOM has been updated – use defer: true:

function redraw () {
canvas.width = drawingApp.get( 'width' );
canvas.height = drawingApp.get( 'height' );
updateCanvas();
}

drawingApp.observe( 'width', redraw, { defer: true });
drawingApp.observe( 'height', redraw, { defer: true });

component.on(eventName, callback) #

Allows you to respond to events:

const listener = component.on( 'thingHappened', event => {
console.log( `A thing happened: ${event.thing}` );
});

// some time later...
listener.cancel();

component.fire(eventName, event) #

The companion to component.on(...):

component.fire( 'thingHappened', {
thing: 'this event was fired'
});

At first glance component.on(...) and component.fire(...) aren’t particularly useful, but it’ll become more so when we learn about nested components.

ℹ️ component.on(...) and component.observe(...) look quite similar, but they have different purposes. Observers are useful for reacting to data flowing through your application and changing continuously over time, whereas events are good for modeling discrete moments such as ‘the user made a selection, and this is what it is’.

component.teardown() #

Removes the component from the DOM and removes any observers and event listeners that were created. This will also fire a teardown event:

component.on( 'teardown', () => {
alert( 'goodbye!' ); // please don't do this
});

component.teardown();

Template syntax #

Rather than reinventing the wheel, Svelte templates are built on foundations that have stood the test of time: HTML, CSS and JavaScript. There’s very little extra stuff to learn.

Tags #

Tags allow you to bind data to your template. Whenever your data changes (for example after component.set(...)), the DOM updates automatically. You can use any JavaScript expression in templates, and it will also automatically update:

<p> +  = </p>

You can also use tags in attributes:

<h1 style='color: ;'></h1>

ℹ️ While tags are delimited using ``, Svelte does not use Mustache syntax. Tags are just JavaScript expressions.

If blocks #

Control whether or not part of your template is rendered by wrapping it in an if block.


<a href='/logout'>log out</a>



<a href='/login'>log in</a>

You can combine the two blocks above with ``:


<a href='/logout'>log out</a>

<a href='/login'>log in</a>

You can also use ``:


<p> is greater than 10</p>

<p> is less than 5</p>

<p> is between 5 and 10</p>

Each blocks #

Iterate over lists of data:

<h1>Cats of YouTube</h1>

<ul>

<li><a target='_blank' href=''></a></li>

</ul>

You can access the index of the current element with expression as name, index:

<div class='grid'>

<div class='row'>

<code class='cell'>
,:
<strong></strong>
</code>

</div>

</div>

Directives #

The last place where Svelte template syntax differs from regular HTML: directives allow you to add special instructions for adding event handlers, two-way bindings, refs and so on. We’ll cover each of those in later stages of this guide – for now, all you need to know is that directives can be identified by the : character:

<p>Count: </p>
<button on:click='set({ count: count + 1 })'>+1</button>

ℹ️ Technically, the : character is used to denote namespaced attributes in HTML. These will not be treated as directives, if encountered.

Scoped styles #

One of Svelte’s key tenets is that components should be self-contained and reusable in different contexts. Because of that, it has a mechanism for scoping your CSS, so that you don’t accidentally clobber other selectors on the page.

Adding styles #

Your component template can have a <style> tag, like so:

<div class='foo'>
Big red Comic Sans
</div>

<style>
.foo {
color: red;
font-size: 2em;
font-family: 'Comic Sans MS';
}
</style>

How it works #

Open the example above in the REPL and inspect the element to see what has happened – Svelte has added a svelte-[uniqueid] attribute to the element, and transformed the CSS selector accordingly. Since no other element on the page can share that selector, anything else on the page with class="foo" will be unaffected by our styles.

This is vastly simpler than achieving the same effect via Shadow DOM and works everywhere without polyfills.

ℹ️ Svelte will add a <style> tag to the page containing your scoped styles. Dynamically adding styles may be impossible if your site has a Content Security Policy. If that’s the case, you can use scoped styles by server-rendering your CSS and using the css: false compiler option (or --no-css with the CLI).

Cascading rules #

The usual cascading mechanism still applies – any global .foo styles would still be applied, and if our template had nested components with class="foo" elements, they would inherit our styles.

ℹ️ Scoped styles are not dynamic – they are shared between all instances of a component. In other words you can’t use `` inside your CSS.

Behaviours #

As well as scoped styles and a template, components can encapsulate behaviours. For that, we add a <script> element and export an object:

<div>
<!-- template goes here -->
</div>

<script>
export default {
// behaviours go here
};
</script>

Default data #

Often, it makes sense for a component to have default data. This should be expressed as a function that returns a POJO:

<p>Count: </p>
<button on:click='set({ count: count + 1 })'>+1</button>

<script>
export default {
data () {
return {
count: 0
};
}
};
</script>

Data supplied at instantiation (i.e. new Component(...)) takes priority over defaults.

ℹ️ The example above, like many of the examples below, uses ES2015 syntax – i.e. data () {...} rather than data: function {...}. While Svelte will generate ES5 code that runs everywhere, it won’t convert your ES2015 code into ES5 – so if you use ES2015 and need to support older browsers, you will need an additional transpilation step in your build process using Babel or Bublé.

Computed properties #

Often, your program will use values that depend on other values – for example, you might have a filtered list, which depends on both the list and the filter. Normally in JavaScript you’d have to add logic to update the dependent property when any of the dependencies change. This is a frequent source of bugs, and it gets worse as your application grows.

Svelte allows you to express these dependencies in computed properties, which are recalculated whenever those dependencies change:

<p>
The time is
<strong>::</strong>
</p>

<script>
export default {
data () {
return {
time: new Date()
};
},

computed: {
hours: time => time.getHours(),
minutes: time => time.getMinutes(),
seconds: time => time.getSeconds()
}
};
</script>

Notice that all we need to do to tell Svelte that hours, minutes and seconds depend on time is include it as a parameter to the function. There’s no costly dependency tracking involved – the dependency graph is resolved at compile time.

ℹ️ computed must be an object literal, and the properties must be function expressions or arrow function expressions.

Lifecycle hooks #

There are two ‘hooks’ provided by Svelte for adding control logic – onrender and onteardown:

<p>
The time is
<strong>::</strong>
</p>

<script>
export default {
onrender () {
this.interval = setInterval( () => {
this.set({ time: new Date() });
}, 1000 );
},

onteardown () {
clearInterval( this.interval );
},

data () {
return {
time: new Date()
};
},

computed: {
hours: time => time.getHours(),
minutes: time => time.getMinutes(),
seconds: time => time.getSeconds()
}
};
</script>

Helpers #

Helpers are simple functions that are used in your template. In the example above, we want to ensure that minutes and seconds are preceded with a 0 if they only have one digit, so we add a leftPad helper:

<p>
The time is
<strong>::</strong>
</p>

<script>
import leftPad from 'left-pad';

export default {
helpers: {
leftPad
},

onrender () {
this.interval = setInterval( () => {
this.set({ time: new Date() });
}, 1000 );
},

onteardown () {
clearInterval( this.interval );
},

data () {
return {
time: new Date()
};
},

computed: {
hours: time => time.getHours(),
minutes: time => time.getMinutes(),
seconds: time => time.getSeconds()
}
};
</script>

Of course, you could use leftPad inside the computed properties instead of in the template. There’s no hard and fast rule about when you should use expressions with helpers versus when you should use computed properties – do whatever makes your component easier for the next developer to understand.

ℹ️ Helper functions should be pure – in other words, they should not have side-effects, and their returned value should only depend on their arguments.

Custom methods #

In addition to the built-in methods, you can add methods of your own:

<script>
export default {
methods: {
say: function ( message ) {
alert( message ); // again, please don't do this
}
}
};
</script>

These become part of the component’s API:

import MyComponent from './MyComponent.html';

var component = new MyComponent({
target: document.querySelector( 'main' )
});

component.say( '👋' );

Methods (whether built-in or custom) can also be called inside event handlers:

<button on:click='say("hello")'>say hello!</button>

Custom event handlers #

Soon, we’ll learn about event handlers – if you want, skip ahead to that section first then come back here!

Most of the time you can make do with the standard DOM events (the sort you’d add via element.addEventListener, such as click) but sometimes you might need custom events to handle gestures, for example.

Custom events are just functions that take a node and a callback as their argument, and return an object with a teardown method that gets called when the element is removed from the page:

<button on:longpress='set({ done: true })'>click and hold</button>


<p>clicked and held</p>


<script>
export default {
events: {
longpress ( node, callback ) {
function onmousedown ( event ) {
const timeout = setTimeout( () => callback( event ), 1000 );

function cancel () {
clearTimeout( timeout );
node.removeEventListener( 'mouseup', cancel, false );
}

node.addEventListener( 'mouseup', cancel, false );
}

node.addEventListener( 'mousedown', onmousedown, false );

return {
teardown () {
node.removeEventListener( 'mousedown', onmousedown, false );
}
};
}
}
};
</script>

Nested components #

So far, we’ve been working with single standalone components. But if you tried to put your entire application in one component it would quickly become unwieldy.

Fortunately, Svelte components can be nested:

<div class='widget-container'>
<Widget foo bar='static' baz=''/>
</div>

<script>
import Widget from './Widget.html';

export default {
data () {
return {
dynamic: 'this can change'
}
},

components: {
Widget
}
};
</script>

The example above is equivalent to the following…

import Widget from './Widget.html';

const widget = new Widget({
target: document.querySelector( '.widget-container' ),
data: {
foo: true,
bar: 'static',
baz: 'this can change'
}
});

…except that Svelte will ensure that the value of baz is kept in sync with the value of dynamic in the parent component, and takes care of tearing down the child component when the parent is torn down.

ℹ️ Component names should be capitalised, following the widely-used JavaScript convention of capitalising constructor names. It’s also an easy way to distinguish components from elements in your template.

Element directives #

Directives are element or component-level instructions to Svelte. They look like attributes, except with a : character.

Event handlers #

In most applications, you’ll need to respond to the user’s actions. In Svelte, this is done with the on:[event] directive.

<p>Count: </p>
<button on:click='set({ count: count + 1 })'>+1</button>

When the user clicks the button, Svelte calls component.set(...) with the provided arguments. You can call any method belonging to the component (whether built-in or custom), and any data property (or computed property) that’s in scope:

<p>Select a category:</p>


<button on:click='select( category )'>select </button>


<script>
export default {
data () {
return {
categories: [
'animal',
'vegetable',
'mineral'
]
}
},

methods: {
select ( name ) {
alert( `selected ${name}` );
}
}
};
</script>

You can also access the event object in the method call:

<div on:mousemove='set({ x: event.clientX, y: event.clientY })'>
coords: ,
</div>

<style>
div {
border: 1px solid purple;
width: 100%;
height: 100%;
}
</style>

Custom events #

You can define your own custom events to handle complex user interactions like dragging and swiping. See the earlier section on custom event handlers for more information.

Component events #

Events are an excellent way for nested components to communicate with their parents. Let’s revisit our earlier example, but turn it into a <CategoryChooser> component:

<!-- CategoryChooser.html -->
<p>Select a category:</p>


<button on:click='fire( "select", { category } )'>select </button>


<script>
export default {
data () {
return {
categories: [
'animal',
'vegetable',
'mineral'
]
}
}
};
</script>

When the user clicks a button, the component will fire a select event, where the event object has a category property. Any component that nests <CategoryChooser> can listen for events like so:

<CategoryChooser on:select='playTwentyQuestions( event.category )'/>

<script>
import CategoryChooser from './CategoryChooser.html';

export default {
components: {
CategoryChooser
},

methods: {
playTwentyQuestions ( category ) {
// TODO implement
}
}
};
</script>

Refs #

Refs are a convenient way to store a reference to particular DOM nodes or components. Declare a ref with ref:[name], and access it inside your component’s methods with this.refs.[name]:

<canvas ref:canvas width='200' height='200'></canvas>

<script>
export default {
onrender () {
const canvas = this.refs.canvas;
const ctx = canvas.getContext( '2d' );

let torndown = false;
this.on( 'teardown', () => torndown = true );

function loop () {
if ( torndown ) return;
requestAnimationFrame( loop );

const imageData = ctx.getImageData( 0, 0, canvas.width, canvas.height );

for ( let p = 0; p < imageData.data.length; p += 4 ) {
const i = p / 4;
const x = i % canvas.width;
const y = i / canvas.height >>> 0;

const t = window.performance.now();

const r = 64 + ( 128 * x / canvas.width ) + ( 64 * Math.sin( t / 1000 ) );
const g = 64 + ( 128 * y / canvas.height ) + ( 64 * Math.cos( t / 1000 ) );
const b = 128;

imageData.data[ p + 0 ] = r;
imageData.data[ p + 1 ] = g;
imageData.data[ p + 2 ] = b;
imageData.data[ p + 3 ] = 255;
}

ctx.putImageData( imageData, 0, 0 );
}

loop();
}
}
</script>

ℹ️ Since only one element or component can occupy a given ref, don’t use them in blocks. It's fine to use them in blocks however.

Two-way binding #

It’s currently fashionable to avoid two-way binding on the grounds that it creates all sorts of hard-to-debug problems and slows your application down, and that a one-way top-down data flow is ‘easier to reason about’. This is in fact high grade nonsense. It’s true that two-way binding done badly has all sorts of issues, and that very large apps benefit from the discipline of a not permitting deeply nested components to muck about with state that might affect distant parts of the app. But when used correctly, two-way binding simplifies things greatly.

Bindings are declared with the bind:[attribute] directive:

<input bind:value='name' placeholder='enter your name'>
<p>Hello !</p>

ℹ️ Two-way binding is not yet fully implemented. Check back soon for the full list of available bindings!

As well as DOM elements, you can bind to component data properties:

<CategoryChooser bind:category='category'/>

If the attribute and the bound property share a name, you can use this shorthand:

<CategoryChooser bind:category/>

Plugins #

TODO…

Server-side rendering #

So far, we’ve discussed creating Svelte components on the client, which is to say the browser. But you can also render Svelte components in Node.js. This can result in better perceived performance as it means the application starts rendering while the page is still downloading, before any JavaScript executes. It also has SEO advantages in some cases, and can be beneficial for people running older browsers that can’t or won’t run your JavaScript for whatever reason.

Rendering HTML #

To use the server-side renderer, we must first register it. This means that when you require a component .html file, it gets intercepted by the SSR compiler:

require( 'svelte/ssr/register' );

After that, you can load components like so:

const thing = require( './components/Thing.html' );

Components have a different API in Node.js – rather than creating instances with set(...) and get(...) methods, a component is an object with a render(data) method which returns HTML (the data object is the same as you would use when instantiating a component in the browser, and is optional):

const data = { answer: 42 };
const html = thing.render( data );

Any default data, computed properties, helpers and nested components will work as expected.

ℹ️ The SSR compiler will generate a CommonJS module for each of your components – meaning that import and export statements are converted into their require and module.exports equivalents. If your components have non-component dependencies, they must also work as CommonJS modules in Node. If you’re using ES2015 modules, we recommend reify for automatically converting them to CommonJS.

Rendering CSS #

You can also render your component’s (scoped) CSS, including that of any nested components:

const { css, components } = thing.renderCss();

You could put the resulting css in a separate stylesheet, or include them in the page inside a <style> tag. If you do this, you will probably want to prevent the client-side compiler from including the CSS again. For svelte-cli, use the --no-css flag. In build tool integrations like rollup-plugin-svelte, pass the css: false option.

ℹ️ The components array contains an object for each nested component that contains styles, allowing you to dedupe styles across multiple top-level components. Most of the time, you won’t need to do this.

TODO… #

This documentation is still a work-in-progress, like Svelte itself.