Svelte is a tool for building fast web applications.
It is similar to JavaScript frameworks such as React, Angular, Vue, and Ractive, which all share a goal of making it easy to build slick interactive user interfaces.
But there's a crucial difference: Svelte converts your app into ideal JavaScript at build time, rather than interpreting your application code at run time. This means you don't pay the performance cost of the framework's abstractions, and you don't incur a penalty when your app first loads.
You can build your entire app with Svelte, or you can add it incrementally to an existing codebase. You can also ship components as standalone packages that work anywhere, without the overhead of a dependency on a conventional framework.
Read the introductory blog post to learn more about Svelte's goals and philosophy.
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:
<h1>Hello {name}!</h1>
Wherever you see REPL links, click through for an interactive example
Svelte turns this into a JavaScript module that you can import into your app:
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.destroy();
Congratulations, you've just learned about half of Svelte's API!
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.
You will need to have Node.js installed, and have some familiarity with the command line
Going to the REPL and pressing the download button on any of the examples will give you a .zip file containing everything you need to run that example locally. Just unzip it, cd
to the directory, and run npm install
and npm run dev
. See this blog post for more information.
degit is a tool for creating projects from templates stored in git repos. Install it globally...
npm install -g degit
...then you can use it to spin up a new project:
degit sveltejs/template my-new-project
cd my-new-project
npm install
npm run dev
You can use any git repo you like — these are the 'official' templates:
Svelte also provides a Command Line Interface, but it's not recommended for production use. The CLI will compile your components to standalone JavaScript files, but won't automatically recompile them when they change, and won't deduplicate code shared between your components. Use one of the above methods instead.
If you've installed svelte
globally, you can use svelte --help
for a complete list of options. Some examples of the more common operations are:
# Generate a JavaScript module from MyComponent.html
svelte compile MyComponent.html > MyComponent.js
svelte compile -i MyComponent.html -o MyComponent.js
# Generate a UMD module from MyComponent.html, inferring its name from the filename ('MyComponent')
svelte compile -f umd MyComponent.html > MyComponent.js
# Generate a UMD module, specifying the name
svelte compile -f umd -n CustomName MyComponent.html > MyComponent.js
# Compile all .html files in a directory
svelte compile -i src/components -o build/components
You can also use npx to use the CLI without installing Svelte globally — just prefix your command with
npx
:npx svelte compile ...
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. This is the
// DOM element your component will be appended to
target: document.querySelector('main'),
// `anchor` is optional.
// The component is inserted immediately before this
// DOM element, which must be a child of `target`
anchor: document.querySelector('main #child'),
// `data` is optional.
// A component can 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.
This updates the component's state with the new values provided and causes the DOM to update. state
must be a plain old JavaScript object (POJO). Any properties not included in state
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'
});
Returns the component's current state:
const { questions, answer } = component.get();
console.log(answer); // 'ask your mother'
This will also retrieve the value of computed properties.
Previous versions of Svelte allowed you to specify a key to retrieve a specific value — this was removed in version 2.
Allows you to respond to events:
const listener = component.on('thingHappened', event => {
console.log(`A thing happened: ${event.thing}`);
});
// some time later...
listener.cancel();
Each component has three built-in events, corresponding to their lifecycle hooks:
component.on('state', ({ changed, current, previous }) => {
console.log('state changed', current);
});
component.on('update', ({ changed, current, previous }) => {
console.log('DOM updated after state change', current);
});
component.on('destroy', () => {
console.log('this component is being destroyed');
});
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 and component events.
Removes the component from the DOM and removes any event listeners that were created. This will also fire a destroy
event:
component.on('destroy', () => {
alert('goodbye!'); // please don't do this
});
component.destroy();
The options used to instantiate the component are available in component.options
.
Check the console.
<script>
export default {
oncreate() {
console.log(this.options);
}
};
</script>
This gives you access to standard options like target
and data
, but can also be used to access any other custom options you may choose to implement for your component.
In nested components, each component has a root
property pointing to the top-level root component – that is, the one instantiated with new MyComponent({...})
.
Earlier versions of Svelte had a
component.observe(...)
method. This was removed in version 2, in favour of theonstate
lifecycle hook, but is still available via svelte-extras.
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.
Svelte version 1 had a slightly different template syntax. You can upgrade older components automatically using svelte-upgrade.
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>{a} + {b} = {a + b}</p>
You can also use tags in attributes:
<h1 style="color: {color};">{color}</h1>
<p hidden={hideParagraph}>You can hide this paragraph.</p>
Boolean attributes like hidden
will be omitted if the tag expression evaluates to false. Attributes will be removed from the element if their value is undefined
or null
.
Ordinary tags render expressions as plain text. If you need your expression interpreted as HTML, wrap it in a special @html
tag:
<p>This HTML: {content}</p>
<p>Renders as: {@html content}</p>
As with regular tags, you can use any JavaScript expression in HTML tags, and it will automatically update the document when your data changes.
HTML is not sanitized before it is rendered! If you are displaying user input, you are responsible for first sanitizing it. Not doing so potentially opens you up to XSS attacks.
Control whether or not part of your template is rendered by wrapping it in an if block.
{#if user.loggedIn}
<a href="/logout">log out</a>
{/if}
{#if !user.loggedIn}
<a href="/login">log in</a>
{/if}
You can combine the two blocks above with {:else}
:
{#if user.loggedIn}
<a href="/logout">log out</a>
{:else}
<a href="/login">log in</a>
{/if}
You can also use {:elseif ...}
:
{#if x > 10}
<p>{x} is greater than 10</p>
{:elseif 5 > x}
<p>{x} is less than 5</p>
{:else}
<p>{x} is between 5 and 10</p>
{/if}
Iterate over lists of data:
<h1>Cats of YouTube</h1>
<ul>
{#each cats as cat}
<li><a target="_blank" href={cat.video}>{cat.name}</a></li>
{:else}
<li>No cats :(</li>
{/each}
</ul>
Else is triggered when the list is empty.
You can access the index of the current element with expression as name, index:
<div class="grid">
{#each rows as row, y}
<div class="row">
{#each columns as column, x}
<code class="cell">
{x + 1},{y + 1}:
<strong>{row[column]}</strong>
</code>
{/each}
</div>
{/each}
</div>
By default, if the list
a, b, c
becomesa, c
, Svelte will remove the third block and change the second fromb
toc
, rather than removingb
. If that's not what you want, use a keyed each block.
You can use destructuring patterns on the elements of the array:
<h1>It's the cats of YouTube again</h1>
<ul>
{#each cats as {name, video} }
<li><a target="_blank" href={video}>{name}</a></li>
{/each}
</ul>
If you want to iterate over an object you can use Object.entries(object)
which returns the object's properties as [key, value]
pairs:
<h1>Cats and Dogs</h1>
{#each Object.entries(animals) as [animal, names]}
<p>{animal}: {names.join(" and ")}</p>
{/each}
You can represent the three states of a Promise — pending, fulfilled and rejected — with an await
block:
{#await promise}
<p>wait for it...</p>
{:then answer}
<p>the answer is {answer}!</p>
{:catch error}
<p>well that's odd</p>
{/await}
<script>
export default {
data() {
return {
promise: new Promise(fulfil => {
setTimeout(() => fulfil(42), 3000);
})
};
}
};
</script>
If the expression in {#await expression}
isn't a promise, Svelte skips ahead to the then
section.
Directives allow you to add special instructions for adding event handlers, bindings, referencing elements 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: {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.
To inspect data as it changes and flows through your app, use a {@debug ...}
tag:
<input bind:value=name>
{@debug name}
<h1>Hello {name}!</h1>
This will log the value of name
whenever it changes. If your devtools are open, changing name
will pause execution and open the debugger.
You can debug multiple values simultaneously ({@debug foo, bar, baz}
), or use {@debug}
to pause execution whenever the surrounding markup is updated.
Debug tags only have an effect when compiling with the
dev: true
compiler option.
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.
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>
Open the example above in the REPL and inspect the element to see what has happened – Svelte has added a svelte-[uniqueid]
class 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).
Styles will only apply to the current component, unless you opt in to cascading with the :global(...)
modifier:
<div>
<Widget/>
</div>
<style>
p {
/* this block will be disregarded, since
there are no <p> elements here */
color: red;
}
div :global(p) {
/* this block will be applied to any <p> elements
inside the <div>, i.e. in <Widget> */
font-weight: bold;
}
</style>
<script>
import Widget from './Widget.html';
export default {
components: { Widget }
};
</script>
Scoped styles are not dynamic – they are shared between all instances of a component. In other words you can't use
{tags}
inside your CSS.
Svelte will identify and remove styles that are not being used in your app. It will also emit a warning so that you can remove them from the source.
For rules not to be removed, they must apply to the component's markup. As far as Svelte is concerned .active
is unused in the following code and should be removed:
<div>
<p ref:paragraph>this text is not bold</p>
</div>
<style>
.active {
color: bold;
}
</style>
<script>
export default {
oncreate() {
const { active } = this.get();
if (active) this.refs.paragraph.classList.add('active');
}
};
</script>
Instead of manually manipulating the DOM, you should always use the class
attribute (or the class directive):
<div>
<p class="{active ? 'active' : ''}">this text is bold</p>
</div>
If that's impossible for some reason, you can use :global(...)
:
<style>
div :global(.active) {
color: bold;
}
</style>
The same applies to the contents of {@html ...}
tags.
If you have a ref on an element, you can use it as a CSS selector. The ref:*
selector has the same specificity as a class or attribute selector.
<div ref:foo>
yeah!
</div>
<style>
ref:foo {
color: magenta;
font-size: 5em;
}
</style>
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>
Often, it makes sense for a component to have default data. This should be expressed as a function that returns a plain JavaScript object:
<p>Count: {count}</p>
<button on:click="set({ count: count + 1 })">+1</button>
<script>
export default {
data() {
return {
count: 0
};
}
};
</script>
Data supplied at instantiation takes priority over defaults. In other words, if we instantiated the component above like so...
const counter = new Counter({
data: {
count: 99
}
});
...then {count}
, or counter.get().count
, would initially be 99 rather than 0.
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>{hours}:{minutes}:{seconds}</strong>
</p>
<script>
export default {
data() {
return {
time: new Date()
};
},
computed: {
hours: ({ time }) => time.getHours(),
minutes: ({ time }) => time.getMinutes(),
seconds: ({ time }) => time.getSeconds()
}
};
</script>
Each function is passed the component's current state object. Because we're using destructuring syntax, the compiler knows that hours
, minutes
and seconds
only need to re-run when time
changes, and not when any other values change. 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. Any external functions used in computed must be wrapped here:
import externalFunc from '_external_file';
export default {
computed: {
externalFunc: ({ dep1, dep2 }) => externalFunc(dep1, dep2);
}
}
Computed properties can of course return functions. For example, we could dynamically generate a filter function for a list of items:
<input bind:value=search>
{#each items.filter(predicate) as word}
<p><strong>{word.slice(0, search.length)}</strong>{word.slice(search.length)}</p>
{:else}
<p>no matches!</p>
{/each}
<script>
export default {
computed: {
predicate: ({ search }) => {
search = search.toLowerCase();
return word => word.startsWith(search);
}
}
};
</script>
There are four 'hooks' provided by Svelte for adding control logic — oncreate
, ondestroy
, onstate
and onupdate
:
<p>
The time is
<strong>{hours}:{minutes}:{seconds}</strong>
</p>
<script>
export default {
onstate({ changed, current, previous }) {
// this fires before oncreate, and on every state change.
// the first time it runs, `previous` is undefined
if (changed.time) {
console.log(`time changed: ${previous && previous.time} -> ${current.time}`);
}
},
oncreate() {
// this fires when the component has been
// rendered to the DOM
console.log('in oncreate');
this.interval = setInterval(() => {
this.set({ time: new Date() });
}, 1000);
},
onupdate({ changed, current, previous }) {
// this fires after oncreate, and after every state change
// once the DOM is synchronised with the data
console.log(`The DOM has been updated`);
},
ondestroy() {
// this fires when the component is
// removed from the DOM
console.log('in ondestroy');
clearInterval(this.interval);
},
data() {
return {
time: new Date()
};
},
computed: {
hours: ({ time }) => time.getHours(),
minutes: ({ time }) => time.getMinutes(),
seconds: ({ time }) => time.getSeconds()
}
};
</script>
You can add event listeners corresponding to
onstate
,onupdate
andondestroy
programmatically — see component.on
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>{hours}:{leftPad(minutes, 2, '0')}:{leftPad(seconds, 2, '0')}</strong>
</p>
<script>
import leftPad from 'left-pad';
export default {
helpers: {
leftPad
},
oncreate() {
this.interval = setInterval(() => {
this.set({ time: new Date() });
}, 1000);
},
ondestroy() {
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.
In addition to the built-in methods, you can add methods of your own:
<p>Try calling <code>app.say('hello!')</code> from the console</p>
<script>
export default {
methods: {
say(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>
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 destroy
method that gets called when the element is removed from the page:
<button on:longpress="set({ done: true })">click and hold</button>
{#if done}
<p>clicked and held</p>
{/if}
<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 {
destroy() {
node.removeEventListener('mousedown', onmousedown, false);
}
};
}
}
};
</script>
Components are assumed to be in the HTML namespace. If your component is designed to be used inside an <svg>
element, you need to specify the namespace:
<svg viewBox="0 0 1000 1000" style="width: 100%; height: 100%;">
<SmileyFace x=70 y=280 size=100 fill="#f4d9c7"/>
<SmileyFace x=800 y=250 size=150 fill="#40250f"/>
<SmileyFace x=150 y=700 size=110 fill="#d2aa7a"/>
<SmileyFace x=875 y=730 size=130 fill="#824e2e"/>
<SmileyFace x=450 y=500 size=240 fill="#d2b198"/>
</svg>
<script>
import SmileyFace from './SmileyFace.html';
export default {
components: { SmileyFace }
};
</script>
<!-- CC-BY-SA — https://commons.wikimedia.org/wiki/File:718smiley.svg -->
<g transform="translate({x},{y}) scale({size / 366.5})">
<circle r=366.5/>
<circle r=336.5 fill={fill}/>
<path d="m-41.5 298.5c-121-21-194-115-212-233v-8l-25-1-1-18h481c6 13 10 27 13 41 13 94-38 146-114 193-45 23-93 29-142 26z"/>
<path d="m5.5 280.5c52-6 98-28 138-62 28-25 46-56 51-87 4-20 1-57-5-70l-423-1c-2 56 39 118 74 157 31 34 72 54 116 63 11 2 38 2 49 0z" fill="#871945"/>
<path d="m-290.5 -24.5c-13-26-13-57-9-85 6-27 18-52 35-68 21-20 50-23 77-18 15 4 28 12 39 23 18 17 30 40 36 67 4 20 4 41 0 60l-6 21z"/>
<path d="m-132.5 -43.5c5-6 6-40 2-58-3-16-4-16-10-10-14 14-38 14-52 0-15-18-12-41 6-55 3-3 5-5 5-6-1-4-22-8-34-7-42 4-57.6 40-66.2 77-3 17-1 53 4 59h145.2z" fill="#fff"/>
<path d="m11.5 -23.5c-2-3-6-20-7-29-5-28-1-57 11-83 15-30 41-52 72-60 29-7 57 0 82 15 26 17 45 49 50 82 2 12 2 33 0 45-1 10-5 26-8 30z"/>
<path d="m198.5 -42.5c4-5 5-34 4-50-2-14-6-24-8-24-1 0-3 2-6 5-17 17-47 13-58-9-7-16-4-31 8-43 4-4 7-8 7-9 0 0-4-2-8-3-51-17-105 20-115 80-3 15 0 43 3 53z" fill="#fff"/>
<path d="m137.5 223.5s-46 40-105 53c-66 15-114-7-114-7s14-76 93-95c76-18 126 49 126 49z" fill="#f9bedd"/>
</g>
<script>
export default {
// you can either use the shorthand 'svg', or the full
// namespace: 'http://www.w3.org/2000/svg'. (I know
// which one I prefer.)
namespace: 'svg',
data() {
return {
x: 100,
y: 100,
size: 100
};
}
};
</script>
As well as containing elements (and if
blocks and each
blocks), Svelte components can contain other Svelte components.
<div class='widget-container'>
<Widget/>
</div>
<script>
import Widget from './Widget.html';
export default {
components: {
Widget
}
};
</script>
<p>I am a nested component</p>
That's similar to doing this...
import Widget from './Widget.html';
const widget = new Widget({
target: document.querySelector('.widget-container')
});
...except that Svelte takes care of destroying the child component when the parent is destroyed, and keeps the two components in sync with props.
Component names must 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.
Props, short for 'properties', are the means by which you pass data down from a parent to a child component — in other words, they're just like attributes on an element:
<div class='widget-container'>
<Widget foo bar="static" baz={dynamic}/>
</div>
<script>
import Widget from './Widget.html';
export default {
components: {
Widget
}
};
</script>
<p>foo: {foo}</p>
<p>bar: {bar}</p>
<p>baz: {baz}</p>
As with element attributes, prop values can contain any valid JavaScript expression.
Often, the name of the property will be the same as the value, in which case we can use a shorthand:
<!-- these are equivalent -->
<Widget foo={foo}/>
<Widget {foo}/>
Note that props are one-way — to get data from a child component into a parent component, use bindings.
<slot>
A component can contain a <slot></slot>
element, which allows the parent component to inject content:
<Box>
<h2>Hello!</h2>
<p>This is a box. It can contain anything.</p>
</Box>
<script>
import Box from './Box.html';
export default {
components: { Box }
};
</script>
<div class="box">
<slot><!-- content is injected here --></slot>
</div>
<style>
.box {
border: 2px solid black;
padding: 0.5em;
}
</style>
The <slot>
element can contain 'fallback content', which will be used if no children are provided for the component:
<Box></Box>
<script>
import Box from './Box.html';
export default {
components: { Box }
};
</script>
<div class="box">
<slot>
<p class="fallback">the box is empty!</p>
</slot>
</div>
<style>
.box {
border: 2px solid black;
padding: 0.5em;
}
.fallback {
color: #999;
}
</style>
You can also have named slots. Any elements with a corresponding slot
attribute will fill these slots:
<ContactCard>
<span slot="name">P. Sherman</span>
<span slot="address">42 Wallaby Way, Sydney</span>
</ContactCard>
<script>
import ContactCard from './ContactCard.html';
export default {
components: { ContactCard }
};
</script>
<div class="contact-card">
<h2><slot name="name"></slot></h2>
<slot name="address">Unknown address</slot>
<br>
<slot name="email">Unknown email</slot>
</div>
<style>
.contact-card {
border: 2px solid black;
padding: 0.5em;
}
</style>
As an alternative to using an import
declaration...
<script>
import Widget from './Widget.html';
export default {
components: { Widget }
};
</script>
...you can write this:
<script>
export default {
components: {
Widget: './Widget.html'
}
};
</script>
Svelte includes a handful of built-in elements with special behaviour.
<svelte:self>
Sometimes, a component needs to embed itself recursively — for example if you have a tree-like data structure. In Svelte, that's accomplished with the <svelte:self>
tag:
{#if countdown > 0}
<p>{countdown}</p>
<svelte:self countdown="{countdown - 1}"/>
{:else}
<p>liftoff!</p>
{/if}
<svelte:component>
If you don't know what kind of component to render until the app runs — in other words, it's driven by state (aka a dynamic component) — you can use <svelte:component>
:
<input type=checkbox bind:checked=foo> foo
<svelte:component this="{foo ? Red : Blue}" name="thing"/>
<script>
import Red from './Red.html';
import Blue from './Blue.html';
export default {
data() {
return { Red, Blue }
}
};
</script>
Note that
Red
andBlue
are items indata
, notcomponents
, unlike if we were doing<Red>
or<Blue>
.
The expression inside the this="{...}"
can be any valid JavaScript expression. For example, it could be a computed property:
<label><input type=radio bind:group=size value=small> small</label>
<label><input type=radio bind:group=size value=medium> medium</label>
<label><input type=radio bind:group=size value=large> large</label>
<svelte:component this={Size}/>
<script>
import Small from './Small.html';
import Medium from './Medium.html';
import Large from './Large.html';
export default {
computed: {
Size: ({size}) => {
if (size === 'small') return Small;
if (size === 'medium') return Medium;
return Large;
}
}
};
</script>
<p style="font-size: 12px">small</p>
<p style="font-size: 18px">medium</p>
<p style="font-size: 32px">LARGE</p>
<svelte:window>
The <svelte:window>
tag gives you a convenient way to declaratively add event listeners to window
. Event listeners are automatically removed when the component is destroyed.
<svelte:window on:keydown="set({ key: event.key, keyCode: event.keyCode })"/>
{#if key}
<p><kbd>{key === ' ' ? 'Space' : key}</kbd> (code {keyCode})</p>
{:else}
<p>click in this window and press any key</p>
{/if}
<style>
kbd {
background-color: #eee;
border: 2px solid #f4f4f4;
border-right-color: #ddd;
border-bottom-color: #ddd;
font-size: 2em;
margin: 0 0.5em 0 0;
padding: 0.5em 0.8em;
font-family: Inconsolata;
}
</style>
You can also bind to certain values — so far innerWidth
, outerWidth
, innerHeight
, outerHeight
, scrollX
, scrollY
and online
:
<svelte:window bind:scrollY=y/>
<div class="background"></div>
<p class="fixed">user has scrolled {y} pixels</p>
<style>
.background {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 9999px;
background: linear-gradient(to bottom, #7db9e8 0%,#0a1d33 100%);
}
.fixed {
position: fixed;
top: 1em;
left: 1em;
color: white;
}
</style>
<svelte:document>
The <svelte:document>
tag, just like <svelte:window>
, gives you a convenient way to declaratively add event listeners to the document
object. This is useful for listening to events that don't fire on window
, such as mouseenter
and mouseleave
.
<svelte:head>
If you're building an application with Svelte — particularly if you're using Sapper — then it's likely you'll need to add some content to the <head>
of your page, such as adding a <title>
element.
You can do that with the <svelte:head>
tag:
<svelte:head>
<title>{post.title} • My blog</title>
</svelte:head>
When server rendering, the <head>
contents can be extracted separately to the rest of the markup.
Directives are element or component-level instructions to Svelte. They look like attributes, except with a :
character.
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: {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>
{#each categories as category}
<button on:click="select(category)">select {category}</button>
{/each}
<script>
export default {
data() {
return {
categories: [
'animal',
'vegetable',
'mineral'
]
}
},
methods: {
select(name) {
alert(`selected ${name}`); // seriously, please don't do this
}
}
};
</script>
You can also access the event
object in the method call:
<div on:mousemove="set({ x: event.clientX, y: event.clientY })">
coords: {x},{y}
</div>
<style>
div {
border: 1px solid purple;
width: 100%;
height: 100%;
}
</style>
The target node can be referenced as this
, meaning you can do this sort of thing:
<input on:focus="this.select()" value="click to select">
While you can invoke methods like event.stopPropagation
directly...
<div on:click="event.stopPropagation()">...</div>
...it gets annoying if you want to combine that with some other behaviour:
<div on:click="setFoo(event)">...</div>
<script>
export default {
methods: {
setFoo(event) {
event.stopPropagation();
event.preventDefault();
this.set({ foo: true });
}
}
};
</script>
For that reason, Svelte lets you use event modifiers:
preventDefault
stopPropagation
passive
— improves scrolling performance on touch/wheel events (Svelte will add it automatically where it's safe to do so)once
— removes the listener after the first invocationcapture
passive
andonce
are not implemented inlegacy
mode
The example above can be achieved with modifiers — no need for a custom method:
<div on:click|stopPropagation|preventDefault="set({ foo: true })">...</div>
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.
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:
<p>Select a category:</p>
{#each categories as category}
<button on:click="fire('select', { category })">select {category}</button>
{/each}
<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) {
alert(`ok! you chose ${category}`);
}
}
};
</script>
Just as this
in an element's event handler refers to the element itself, in a component event handler this
refers to the component firing the event.
There is also a shorthand for listening for and re-firing an event unchanged.
<!-- these are equivalent -->
<Widget on:foo="fire('foo', event)"/>
<Widget on:foo/>
Since component events do not propagate as DOM events do, this can be used to pass events through intermediate components. This shorthand technique also applies to element events (on:click
is equivalent to on:click="fire('click', event)"
).
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>
import createRenderer from './createRenderer.js';
export default {
oncreate() {
const canvas = this.refs.canvas;
const ctx = canvas.getContext('2d');
const renderer = createRenderer(canvas, ctx);
this.on('destroy', renderer.stop);
}
}
</script>
Since only one element or component can occupy a given
ref
, don't use them in{#each ...}
blocks. It's fine to use them in{#if ...}
blocks however.
Note that you can use refs in your <style>
blocks — see Special selectors.
Transitions allow elements to enter and leave the DOM gracefully, rather than suddenly appearing and disappearing.
<input type=checkbox bind:checked=visible> visible
{#if visible}
<p transition:fade>fades in and out</p>
{/if}
<script>
import { fade } from 'svelte-transitions';
export default {
transitions: { fade }
};
</script>
Transitions can have parameters — typically delay
and duration
, but often others, depending on the transition in question. For example, here's the fly
transition from the svelte-transitions package:
<input type=checkbox bind:checked=visible> visible
{#if visible}
<p transition:fly="{y: 200, duration: 1000}">flies 200 pixels up, slowly</p>
{/if}
<script>
import { fly } from 'svelte-transitions';
export default {
transitions: { fly }
};
</script>
An element can have separate in
and out
transitions:
<input type=checkbox bind:checked=visible> visible
{#if visible}
<p in:fly="{y: 50}" out:fade>flies up, fades out</p>
{/if}
<script>
import { fade, fly } from 'svelte-transitions';
export default {
transitions: { fade, fly }
};
</script>
Transitions are simple functions that take a node
and any provided parameters
and return an object with the following properties:
duration
— how long the transition takes in millisecondsdelay
— milliseconds before the transition startseasing
— an easing functioncss
— a function that accepts an argument t
between 0 and 1 and returns the styles that should be applied at that momenttick
— a function that will be called on every frame, with the same t
argument, while the transition is in progressOf these, duration
is required, as is either css
or tick
. The rest are optional. Here's how the fade
transition is implemented, for example:
<input type=checkbox bind:checked=visible> visible
{#if visible}
<p transition:fade>fades in and out</p>
{/if}
<script>
export default {
transitions: {
fade(node, { delay = 0, duration = 400 }) {
const o = +getComputedStyle(node).opacity;
return {
delay,
duration,
css: t => `opacity: ${t * o}`
};
}
}
};
</script>
If the
css
option is used, Svelte will create a CSS animation that runs efficiently off the main thread. Therefore if you can achieve an effect usingcss
rather thantick
, you should.
As we've seen, data can be passed down to elements and components with attributes and props. Occasionally, you need to get data back up; for that we use bindings.
Component bindings keep values in sync between a parent and a child:
<Widget bind:childValue=parentValue/>
Whenever childValue
changes in the child component, parentValue
will be updated in the parent component and vice versa.
If the names are the same, you can shorten the declaration:
<Widget bind:value/>
Use component bindings judiciously. They can save you a lot of boilerplate, but will make it harder to reason about data flow within your application if you overuse them.
Element bindings make it easy to respond to user interactions:
<h1>Hello {name}!</h1>
<input bind:value=name>
Some bindings are one-way, meaning that the values are read-only. Most are two-way — changing the data programmatically will update the DOM. The following bindings are available:
Name | Applies to | Kind |
---|---|---|
value |
<input> <textarea> <select> |
Two-way |
checked indeterminate |
<input type=checkbox> |
Two-way |
group (see note) |
<input type=checkbox> <input type=radio> |
Two-way |
currentTime paused played volume |
<audio> <video> |
Two-way |
buffered duration seekable |
<audio> <video> |
One-way |
offsetWidth offsetHeight clientWidth clientHeight |
All block-level elements | One-way |
scrollX scrollY |
<svelte:window> |
Two-way |
online innerWidth innerHeight outerWidth outerHeight |
<svelte:window> |
One-way |
'group' bindings allow you to capture the current value of a set of radio inputs, or all the selected values of a set of checkbox inputs.
Here is a complete example of using two way bindings with a form:
<form on:submit="handleSubmit(event)">
<input bind:value=name type=text>
<button type=submit>Say hello</button>
</form>
<script>
export default {
methods: {
handleSubmit(event) {
// prevent the page from reloading
event.preventDefault();
const { name } = this.get();
alert(`Hello ${name}!`);
}
}
};
</script>
'two way' bindings allow you to update a value in a nested property as seen in checkbox input.
Actions let you decorate elements with additional functionality. Actions are functions which may return an object with lifecycle methods, update
and destroy
. The action will be called when its element is added to the DOM.
Use actions for things like:
<img use:lazyload data-src='giant-photo.jpg'/>
<button on:click="toggleLanguage()" use:tooltip="translations[language].tooltip">
{language}
</button>
<script>
export default {
actions: {
tooltip(node, text) {
const tooltip = document.createElement('div');
tooltip.textContent = text;
Object.assign(tooltip.style, {
position: 'absolute',
background: 'black',
color: 'white',
padding: '0.5em 1em',
fontSize: '12px',
pointerEvents: 'none',
transform: 'translate(5px, -50%)',
borderRadius: '2px',
transition: 'opacity 0.4s'
});
function position() {
const { top, right, bottom } = node.getBoundingClientRect();
tooltip.style.top = `${(top + bottom) / 2}px`;
tooltip.style.left = `${right}px`;
}
function append() {
document.body.appendChild(tooltip);
tooltip.style.opacity = 0;
setTimeout(() => tooltip.style.opacity = 1);
position();
}
function remove() {
tooltip.remove();
}
node.addEventListener('mouseenter', append);
node.addEventListener('mouseleave', remove);
return {
update(text) {
tooltip.textContent = text;
position();
},
destroy() {
tooltip.remove();
node.removeEventListener('mouseenter', append);
node.removeEventListener('mouseleave', remove);
}
}
}
},
methods: {
toggleLanguage() {
const { language } = this.get();
this.set({
language: language === 'english' ? 'latin' : 'english'
});
}
}
};
</script>
Classes let you toggle element classes on and off. To use classes add the directive class
followed by a colon and the class name you want toggled (class:the-class-name="anExpression"
). The expression inside the directive's quotes will be evaluated and toggle the class on and off depending on the truthiness of the expression's result. You can only add class directives to elements.
This example adds the class active
to <li>
elements when the url
property matches the path their links target.
<ul class="links">
<li class:active="url === '/'"><a href="/" on:click="goto(event)">Home</a></li>
<li class:active="url.startsWith('/blog')"><a href="/blog/" on:click="goto(event)">Blog</a></li>
<li class:active="url.startsWith('/about')"><a href="/about/" on:click="goto(event)">About</a></li>
</ul>
<script>
export default {
methods: {
goto(event) {
event.preventDefault();
this.set({ url: event.target.pathname });
}
}
}
</script>
<style>
.links {
list-style: none;
}
.links li {
float: left;
padding: 10px;
}
/* classes added this way are processed with encapsulated styles, no need for :global() */
.active {
background: #eee;
}
</style>
Classes will work with an existing class attribute on your element. If you find yourself adding multiple ternary statements inside a class attribute, classes can simplify your component. Classes are recognized by the compiler and scoped correctly.
If your class name is the same as a property in your component's state, you can use the shorthand of a class binding which drops the expression (class:myProp
).
Note that class names with dashes in them do not usually make good shorthand classes since the property will also need a dash in it. The example below uses a computed property to make working with this easier, but it may be easier to not use the shorthand in cases like this.
<div class:active class:is-selected class:isAdmin>
<p>Active? {active}</p>
<p>Selected? {isSelected}</p>
</div>
<button on:click="set({ active: !active })">Toggle Active</button>
<button on:click="set({ isSelected: !isSelected })">Toggle Selected</button>
<button on:click="set({ isAdmin: !isAdmin })">Toggle Admin</button>
<script>
export default {
computed: {
// Because shorthand reflects the var name, you must use component.set({ "is-selected": true }) or use a computed
// property like this. It might be better to avoid shorthand for class names which are not valid variable names.
"is-selected": ({ isSelected }) => isSelected
}
}
</script>
<style>
div {
width: 300px;
border: 1px solid #ccc;
background: #eee;
margin-bottom: 10px;
}
.active {
background: #fff;
}
.is-selected {
border-color: #99bbff;
box-shadow: 0 0 6px #99bbff;
}
.isAdmin {
outline: 2px solid red;
}
</style>
Svelte can be extended with plugins and extra methods.
The svelte-transitions package includes a selection of officially supported transition plugins, such as fade, fly and slide. You can include them in a component like so:
<label>
<input type=checkbox bind:checked=visible> visible
</label>
{#if visible}
<!-- use `in`, `out`, or `transition` (bidirectional) -->
<div transition:fly="{y:20}">hello!</div>
{/if}
<script>
import { fly } from 'svelte-transitions';
export default {
transitions: { fly }
};
</script>
The svelte-extras package includes a handful of methods for tweening (animating), manipulating arrays and so on.
<input bind:value=newTodo placeholder="buy milk">
<button on:click="push('todos', newTodo)">add todo</button>
<ul>
{#each todos as todo, i}
<li>
<button on:click="splice('todos', i, 1)">x</button>
{todo}
</li>
{/each}
</ul>
<style>
ul {
list-style: none;
padding: 0;
}
li button {
color: rgb(200,0,0);
background: rgba(200,0,0,0.1);
border-color: rgba(200,0,0,0.2);
padding: 0.2em 0.5em;
}
</style>
<script>
import { push, splice } from 'svelte-extras';
export default {
data() {
return {
newTodo: '',
todos: []
};
},
methods: {
push,
splice
}
};
</script>
In some situations, you might want to add static properties to your component constructor. For that, we use the setup
property:
<p>check the console!</p>
<script>
export default {
setup(MyComponent) {
// someone importing this component will be able
// to access any properties or methods defined here:
//
// import MyComponent from './MyComponent.html';
// console.log(MyComponent.ANSWER); // 42
MyComponent.ANSWER = 42;
},
oncreate() {
console.log('the answer is', this.constructor.ANSWER);
console.dir(this.constructor);
}
};
</script>
If your component definition includes a preload
function, it will be attached to the component constructor as a static method. It doesn't change the behaviour of your component in any way — instead, it's a convention that allows systems like Sapper to delay rendering of a component until its data is ready.
See the section on preloading in the Sapper docs for more information.
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.
If you're using the Svelte compiler, whether directly or via an integration like rollup-plugin-svelte or svelte-loader, you can tell it to generate a server-side component by passing the generate: 'ssr'
option:
const { js } = svelte.compile(source, {
generate: 'ssr' // as opposed to 'dom', the default
});
Alternatively, an easy way to use the server-side renderer is to register it:
require('svelte/ssr/register');
Now you can require
your components:
const Thing = require('./components/Thing.html');
If you prefer to use a different file extension, you can pass options like so:
require('svelte/ssr/register')({
extensions: ['.svelte']
});
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, options)
method:
require('svelte/ssr/register');
const Thing = require('./components/Thing.html');
const data = { answer: 42 };
const { html, css, head } = Thing.render(data);
All your default data, computed properties, helpers and nested components will work as expected.
Any oncreate
functions or component methods will not run — these only apply to client-side components.
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 theesm
module for automatically converting them to CommonJS.
If your components use stores, use the second argument:
const { Store } = require('svelte/store.umd.js');
const { html } = Thing.render(data, {
store: new Store({
foo: 'bar'
})
});
You can also extract any scoped styles that are used by the component or its children:
const { css } = Thing.render(data);
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 the CLI, use the --no-css
flag. In build tool integrations like rollup-plugin-svelte
, pass the css: false
option.
<head>
contentsIf your component, any of its children, use the <svelte:head>
component, you can extract the contents:
const { head } = Thing.render(data);
Svelte components have built-in state management via the get
and set
methods. But as your application grows beyond a certain size, you may find that passing data between components becomes laborious.
For example, you might have an <Options>
component inside a <Sidebar>
component that allows the user to control the behaviour of a <MainView>
component. You could use bindings or events to 'send' information up from <Options>
through <Sidebar>
to a common ancestor — say <App>
— which would then have the responsibility of sending it back down to <MainView>
. But that's cumbersome, especially if you decide you want to break <MainView>
up into a set of smaller components.
Instead, a popular solution to this problem is to use a global store of data that cuts across your component hierarchy. React has Redux and MobX (though these libraries can be used anywhere, including with Svelte), and Vue has Vuex.
Svelte has Store
. Store
can be used in any JavaScript app, but it's particularly well-suited to Svelte apps.
Import Store
from svelte/store.js
(remember to include the curly braces, as it's a named import), then create a new store with some (optional) data:
import { Store } from 'svelte/store.js';
const store = new Store({
name: 'world'
});
Each instance of Store
has get
, set
, on
and fire
methods that behave identically to their counterparts on a Svelte component:
const { name } = store.get(); // 'world'
store.on('state', ({ current, changed, previous }) => {
console.log(`hello ${current.name}`);
});
store.set({ name: 'everybody' }); // 'hello everybody'
Let's adapt our very first example:
<h1>Hello {$name}!</h1>
<Greeting/>
<script>
import Greeting from './Greeting.html';
export default {
components: { Greeting }
};
</script>
<p>It's nice to see you, {$name}</p>
import App from './App.html';
import { Store } from 'svelte/store.js';
const store = new Store({
name: 'world'
});
const app = new App({
target: document.querySelector('main'),
store
});
window.store = store; // useful for debugging!
There are three important things to notice:
store
to new App(...)
instead of data
$name
instead of name
. The $
prefix tells Svelte that name
is a store property<Greeting>
is a child of <App>
, it also has access to the store. Without it, <App>
would have to pass the name
property down as a component property (<Greeting name={name}/>
)Components that depend on store properties will re-render whenever they change.
As an alternative to adding the store
option when instantiating, the component itself can declare a dependency on a store:
<h1>Hello {$name}!</h1>
<Greeting/>
<script>
import Greeting from './Greeting.html';
import store from './store.js';
export default {
store: () => store,
components: { Greeting }
};
</script>
<p>It's nice to see you, {$name}</p>
import { Store } from 'svelte/store.js';
export default new Store({ name: 'world' });
Note that the store
option is a function that returns a store, rather than the store itself — this provides greater flexibility.
Just like components, stores can have computed properties:
store = new Store({
width: 10,
height: 10,
depth: 10,
density: 3
});
store.compute(
'volume',
['width', 'height', 'depth'],
(width, height, depth) => width * height * depth
);
store.get().volume; // 1000
store.set({ width: 20 });
store.get().volume; // 2000
store.compute(
'mass',
['volume', 'density'],
(volume, density) => volume * density
);
store.get().mass; // 6000
The first argument is the name of the computed property. The second is an array of dependencies — these can be data properties or other computed properties. The third argument is a function that recomputes the value whenever the dependencies change.
A component that was connected to this store could reference {$volume}
and {$mass}
, just like any other store property.
Each component gets a reference to this.store
. This allows you to attach behaviours in oncreate
...
<script>
export default {
oncreate() {
const listener = this.store.on('state', ({ current }) => {
// ...
});
// listeners are not automatically removed — cancel
// them to prevent memory leaks
this.on('destroy', listener.cancel);
}
};
</script>
...or call store methods in your event handlers, using the same $
prefix as data properties:
<button on:click="$set({ muted: true })">
Mute audio
</button>
Store
doesn't have a concept of actions or commits, like Redux and Vuex. Instead, state is always updated with store.set(...)
.
You can implement custom logic by subclassing Store
:
class TodoStore extends Store {
addTodo(description) {
const todo = {
id: generateUniqueId(),
done: false,
description
};
const todos = this.get().todos.concat(todo);
this.set({ todos });
}
toggleTodo(id) {
const todos = this.get().todos.map(todo => {
if (todo.id === id) {
return {
id,
done: !todo.done,
description: todo.description
};
}
return todo;
});
this.set({ todos });
}
}
const store = new TodoStore({
todos: []
});
store.addTodo('Finish writing this documentation');
Methods can update the store asynchronously:
class NasdaqTracker extends Store {
async fetchStockPrices(ticker) {
const token = this.token = {};
const prices = await fetch(`/api/prices/${ticker}`).then(r => r.json());
if (token !== this.token) return; // invalidated by subsequent request
this.set({ prices });
}
}
const store = new NasdaqTracker();
store.fetchStockPrices('AMZN');
You can call these methods in your components, just like the built-in methods:
<input
placeholder="Enter a stock ticker"
on:change="$fetchStockPrices(this.value)"
>
You can bind to store values just like you bind to component values — just add the $
prefix:
<!-- global audio volume control -->
<input bind:value=$volume type=range min=0 max=1 step=0.01>
Just as in templates, you can access store properties in component computed properties by prefixing them with $
:
{#if isVisible}
<div class="todo {todo.done ? 'done': ''}">
{todo.description}
</div>
{/if}
<script>
export default {
computed: {
// `todo` is a component property, `$filter` is
// a store property
isVisible: ({ todo, $filter }) => {
if ($filter === 'all') return true;
if ($filter === 'done') return todo.done;
if ($filter === 'pending') return !todo.done;
}
}
};
</script>
The Svelte compiler knows which store properties your components are interested in (because of the $
prefix), and writes code that only listens for changes to those properties. Because of that, you needn't worry about having many properties on your store, even ones that update frequently — components that don't use them will be unaffected.
Some developers like to use non-standard languages such as Pug, Sass or CoffeeScript.
It's possible to use these languages, or anything else that can be converted to HTML, CSS and JavaScript, using preprocessors.
Svelte exports a preprocess
function that takes some input source code and returns a Promise for a standard Svelte component, ready to be used with svelte.compile
:
const svelte = require('svelte');
const input = fs.readFileSync('App.html', 'utf-8');
svelte.preprocess(input, {
filename: 'App.html', // this is passed to each preprocessor
markup: ({ content, filename }) => {
return {
code: '<!-- some HTML -->',
map: {...}
};
},
style: ({ content, attributes, filename }) => {
return {
code: '/* some CSS */',
map: {...}
};
},
script: ({ content, attributes, filename }) => {
return {
code: '// some JavaScript',
map: {...}
};
}
}).then(preprocessed => {
fs.writeFileSync('preprocessed/App.html', preprocessed.toString());
const { js } = svelte.compile(preprocessed);
fs.writeFileSync('compiled/App.js', js.code);
});
The markup
preprocessor, if specified, runs first. The content
property represents the entire input string.
The style
and script
preprocessors receive the contents of the <style>
and <script>
elements respectively, along with any attributes
on those elements (e.g. <style lang='scss'>
).
All three preprocessors are optional. Each should return a { code, map }
object or a Promise that resolves to a { code, map }
object, where code
is the resulting string and map
is a sourcemap representing the transformation.
The returned
map
objects are not currently used by Svelte, but will be in future versions
Many build tool plugins, such as rollup-plugin-svelte and svelte-loader, allow you to specify preprocess
options, in which case the build tool will do the grunt work.
Associating a key with a block allows Svelte to be smarter about how it adds and removes items to and from a list. To do so, add an (expression)
that uniquely identifies each member of the list:
{#each people as person (person.name)}
<div>{person.name}</div>
{/each}
It's easier to show the effect of this than to describe it. Open the following example in the REPL:
<button on:click="update()">update</button>
<section>
<h2>Keyed</h2>
{#each people as person (person.name)}
<div transition:slide>{person.name}</div>
{/each}
</section>
<section>
<h2>Non-keyed</h2>
{#each people as person}
<div transition:slide>{person.name}</div>
{/each}
</section>
<style>
button {
display: block;
}
section {
width: 10em;
float: left;
}
</style>
<script>
import { slide } from 'svelte-transitions';
var people = ['Alice', 'Barry', 'Cecilia', 'Douglas', 'Eleanor', 'Felix', 'Grace', 'Horatio', 'Isabelle'];
function random() {
return people
.filter(() => Math.random() < 0.5)
.map(name => ({ name }))
}
export default {
data() {
return { people: random() };
},
methods: {
update() {
this.set({ people: random() });
}
},
transitions: { slide }
};
</script>
If you're using server-side rendering, it's likely that you'll need to create a client-side version of your app on top of the server-rendered version. A naive way to do that would involve removing all the existing DOM and rendering the client-side app in its place:
import App from './App.html';
const target = document.querySelector('#element-with-server-rendered-html');
// avoid doing this!
target.innerHTML = '';
new App({
target
});
Ideally, we want to reuse the existing DOM instead. This process is called hydration. First, we need to tell the compiler to include the code necessary for hydration to work by passing the hydratable: true
option:
const { js } = svelte.compile(source, {
hydratable: true
});
(Most likely, you'll be passing this option to rollup-plugin-svelte or svelte-loader.)
Then, when we instantiate the client-side component, we tell it to use the existing DOM with hydrate: true
:
import App from './App.html';
const target = document.querySelector('#element-with-server-rendered-html');
new App({
target,
hydrate: true
});
It doesn't matter if the client-side app doesn't perfectly match the server-rendered HTML — Svelte will repair the DOM as it goes.
Because arrays and objects are mutable, Svelte must err on the side of caution when deciding whether or not to update things that refer to them.
But if all your data is immutable, you can use the { immutable: true }
compiler option to use strict object comparison (using ===
) everywhere in your app. If you have one component that uses immutable data you can set it to use the strict comparison for just that component.
In the example below, searchResults
would normally be recalculated whenever items
might have changed, but with immutable: true
it will only update when items
has definitely changed. This can improve the performance of your app.
{#each searchResults as item}
<div>{item.name}</div>
{/each}
<script>
import FuzzySearch from 'fuzzy-search';
export default {
immutable: true,
computed: {
searchResults: ({ searchString, items }) => {
if (!searchString) return items;
const searcher = new FuzzySearch(items, ['name', 'location']);
return searcher.search(searchString);
}
}
}
</script>
Here's a live example showing the effect of immutable: true
.
Custom elements are an emerging web standard for creating DOM elements that encapsulate styles and behaviours, much like Svelte components. They are part of the web components family of specifications.
Most browsers need polyfills for custom elements. See caniuse for more details
Svelte components can be used as custom elements by doing the following:
tag
name. The value must contain a hyphen (hello-world
in the example below)customElement: true
in the compiler configuration<h1>Hello {name}!</h1>
<script>
export default {
tag: 'hello-world'
};
</script>
Importing this file will now register a globally-available <hello-world>
custom element that accepts a name
property:
import './HelloWorld.html';
document.body.innerHTML = `<hello-world name="world"/>`;
const el = document.querySelector('hello-world');
el.name = 'everybody';
See svelte-custom-elements.surge.sh (source here) for a larger example.
The compiled custom elements are still full-fledged Svelte components and can be used as such:
el.get().name === el.name; // true
el.set({ name: 'folks' }); // equivalent to el.name = 'folks'
One crucial difference is that styles are fully encapsulated — whereas Svelte will prevent component styles from leaking out, custom elements use shadow DOM which also prevents styles from leaking in.
<slot>
Custom elements can use slots to place child elements, just like regular Svelte components.
You can dispatch events inside custom elements to pass data out:
// inside a component method
const event = new CustomEvent('message', {
detail: 'Hello parent!',
bubbles: true,
cancelable: true,
composed: true // makes the event jump shadow DOM boundary
});
this.dispatchEvent(event);
Other parts of the application can listen for these events with addEventListener
:
const el = document.querySelector('hello-world');
el.addEventListener('message', event => {
alert(event.detail);
});
Note the
composed: true
attribute of the custom event. It enables the custom DOM event to cross the shadow DOM boundary and enter into main DOM tree.
Svelte will determine, from the template and computed
values, which properties the custom element has — for example, name
in our <hello-world>
example. You can specify this list of properties manually, for example to restrict which properties are 'visible' to the rest of your app:
export default {
tag: 'my-thing',
props: ['foo', 'bar']
};
Earlier, we saw the use of customElement: true
to instruct the Svelte compiler to generate a custom element using the tag
and (optional) props
declared inside the component file.
Alternatively, you can pass tag
and props
direct to the compiler:
const { js } = svelte.compile(source, {
customElement: {
tag: 'overridden-tag-name',
props: ['yar', 'boo']
}
});
These options will override the component's own settings, if any.
Custom elements use ES2015 classes (MyThing extends HTMLElement
). Make sure you don't transpile the custom element code to ES5, and use a ES2015-aware minifier such as uglify-es.
If you do need ES5 support, make sure to use Reflect.construct
aware transpiler plugin such as babel-plugin-transform-builtin-classes and a polyfill like custom-elements-es5-adapterjs.
<noscript>
If you use <noscript>
tags in a component, Svelte will only render them in SSR mode. The DOM compiler will strip them out, since you can't create the component without JavaScript, and <noscript>
has no effect if JavaScript is available.
This documentation is still a work-in-progress, like Svelte itself. If there are particular things that are missing or could be improved, then please raise an issue on GitHub!