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
- Component API
- Template syntax
- Scoped styles
- Behaviours
- Element directives
- Plugins
- Server-side rendering
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 doingractive.set('foo', 'bar')
you must always doractive.set({foo: 'bar'})
, and you cannot set nested keypaths directly. Itâs also very similar to ReactâssetState
, 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')})
, andfoo
is an object or array, anyfoo
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(...)
andcomponent.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 thecss: 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 thandata: 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 inblocks. 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
andexport
statements are converted into theirrequire
andmodule.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.