Joshua's Docs - Svelte JS - Cheatsheet and Misc Notes
Light

Resources

What & Link Type
Svelte - Official Docs Official Docs
Svelte - Official Tutorials
- Use menu to quickly switch between
- I've published a single-page compilation
Tutorials
Svelte - Quick Start Guide Guide
Svelte - v2 vs v3 Cheatsheet Cheatsheet
Twitter thread - SvelteJS differences, compared to ReactJS Twitter Thread / Cheatsheet
Flavio Copes: Free Svelte Handbook
- Online Version
- PDF Download
Guide / eBook

Basic Component Syntax

See Docs: "Component Format".

<script>
// Imports, exports, logic, assignments, reactivity, etc.
</script>

<style>
/* Styles 🎨! */
</style>

<!-- Markup (any number of elements, components, etc.) -->

Svelte is very flexible in its SFC approach; none of the sections are mandatory, and you can even have multiple top-level elements. No fragments necessary!

Svelte for React Devs

If you are coming from React (or Preact), you might find it helpful to see how methods and features you used in React align with Svelte built-ins (and are often, IMHO, much easier to use 😊):

What React Svelte
Using JS Values in Render

JSX uses curly braces for JavaScript expressions that should be evaluated (docs). Example:

const name = 'Joshua';
const nameTag = <p>Hello, my name is {name}</p>

This can also be used for attributes:

const name = 'Joshua';
const userPic = <img src="./profile.jpg" alt={`user ${name}`} />;

Svelte uses the same curly brace syntax as React / JSX:

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

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

However, many might prefer the shortcuts and added flexibility that Svelte offers around this syntax in comparison with React. For example, if your variable name is the same as the attribute you are assigning it to, you can skip writting attr={attr}, and just write {attr}:

<script>
	let src = 'https://via.placeholder.com/50';
</script>

<img alt="Placeholder" {src} />

Running code when a local state variable changes

Various different ways:

  • useEffect() hook
  • Relying on re-renders caused by state change
  • Using setState callback
  • Using class lifecycle hooks (componentWillUpdate, componentDidUpdate)

For local variables, Reactive Statements are what you probably want

$: {
	console.log(`name has been changed to ${name}`);
}

For store changes, you can use derived callbacks.

Getting reference to an element inside the component
  • Create a ref var

  • Bind / attach to element in your render section: <MyElement ref={myRef}/>

Also, see: Docs: "Refs and the DOM"

Use bind:this pattern on component. E.g:

<Bus bind:this={busComponent} >

Warning: The value of a variable that is bound to a component will actually be undefined until the component mounts, so you will need to wrap in onMount() if you are trying to use the variable early on.

Using Fragments (empty wrappers that don't output DOM nodes)

<React.Fragment>, or, simply <></>

Docs: React Fragments

Not necessary!

Unlike React, Svelte does not require a single parent node in each component; you can have multiple root-level nodes in Svelte, or even a completely empty component!

Conditional Rendering

Lots of different approaches.

Very common tricks:

// `&&` operator
{isLoggedOut && <p>Please Log In</p>}

// Ternary Operator
{isLoggedOut ? <p>Please Log In</p> : <p>Welcome back!</p>}

There are other approaches, but easiest is to wrap in a if block:

{#if isLoggedOut}
	<p>Please Log In</p>
{/if}

If you want to emulate the React ternary operator approach, just add an {:else} or {:else if} block, before the {/if} end statement.

Rending Arrays

Format your data as an array of jsx, and use it as {jsxArr} withn render()

However, you also have to make sure that each component within the JSX array also has a unique key attribute - see docs.

Use the {#each} block syntax, such as {#each teamMembers as member}.

Similar to React, you should pass a key to allow for data updates.

Dynamically Rendering Components

If you are dynamically returning components as variables from the script section of a Svelte SFC, and want to dynamically render them, you cannot simply render them with brace syntax (like {myComponent}).

The way to do this is with the <svelte:component> element. (Tutorial).

The syntax is:

<svelte:component this={myComponentVar} />

Working with Images

I have a blog post on working with SVGs in Svelte, which also applies to general usage of images in Svelte.

The short summary is that your options are:

  • Put images in /public and then reference them by path

    • or
  • Put images in /src, and use a bundler

Using Reactivity

Svelte reactivity and state is very powerful and pleasant to use, but the syntax can take a little getting used to at first.

I'll summarize below, but the docs and tutorials really do the best job of explaining it.

My summary:

  • You get out-of-the-box reactivity with top-level variables that are:

    • Changed directly via assignment
  • For computed / derived properties, use $: to mark variables or entire statements as being reactive

Here is an example that shows both top-level reactivity, and the more advanced derived reactivity:

Show / Hide Example
<script>
	// This is *automatically* reactive!
	let count = 0;
	// Since the value of this variable is derived on another,
	// and not updated through assignment, we need to mark it
	// with the `$:` prefix to mark it as reactive
	$: isEven = count % 2 === 0;
	
	// If we have a whole bunch of logic that is derived
	// on value changes, we can carry it all out within a
	// single reactive statement block!
	let digits = 0;
	let divisibleByFour = false;
	let hex = `0x0`;
	$: {
		console.log(`count has changed`, count);
		digits = count.toString().length;
		divisibleByFour = count % 4 === 0;
		hex = `0x${count.toString(16)}`;
	}
</script>

<button on:click={() => count++}>Click Me!
</button>

<p>Button clicked {count} times.</p>

<p>Count is {isEven ? `` : `not `} even.</p>

<p>Length = {digits}, divisibleByFour = {divisibleByFour}, hex = {hex}</p>

Working with CSS - Styling and Class Names

CSS - Passing Through Class Names

This is a littler tricker than it may seem, due to Svelte's style scoping and class optimization. You can use any of:

  • In the child, use:

    <script>
    	let classStr = '';
    	export {classStr as class};
    </script>
    
    <div class={classStr}></div>
    • Problem: If you have styles in the parent that are scoped to the class (e.g. myClass passed via classStr (as class prop) they will be optimized out since the compiler thinks the class is unused within the parent

      • Trick: You can use :global(.myClass) {} in the parent to preserve myClass. The downside is that it "pollutes" that component tree; if that class is used in any sub-components, the styles will also be applied...
  • In the child, use class="{$$props.class || ''}"

    • This is not recommended due to optimization issues with $$props
  • For more tips and discussion, see Issue #2870

Passing Components Around

The main ways to have a component accept other components, dynamically, is via either the Slot API or via props, with Slots being generally preferred.

Passing Components via Slots

The <slot> API is generally the easiest way to pass components (or elements) through, from parent to child.

E.g.:

<!-- Parent -->
<MyChild>
	<!-- Anything put here will get passed through as the default slot -->
	<p>Hello!</p>
</MyChild>


<!-- MyChild.svelte -->
<div>
	<slot>
</div>

There is a lot more to slots than just the default slot and above use-case. You can have named slots, a dynamic slot object ($$slots), and even bi-directional data-passing, with <slot let:name={value}> to pass values back to the parent.

Passing Components via Props

Once you have imported a Svelte component, you can actually pass it around just like any other value, including the use-case of passing it to another component via props.

I've put together a minimal but fully-functional example, within the Svelte REPL.

For a very verbose approach, I suppose you could pass actual DOM nodes (e.g. export let children: HTMLDivElement[];)

If you are using TypeScript, you will find some handy component types exported from the base of the main Svelte package. E.g. import type {SvelteComponent, SvelteComponentTyped} from 'svelte'. And remember to use typeof with them - e.g. type Component = typeof SvelteComponentTyped;

Passing Data Around

When passing data around your application, you typically have 4 main built-in options, plus browser globals

  • Svelte Built-Ins

  • Browser Built-Ins: global variables (window.myVar), persistence APIs (IndexedDB, localStorage, cookies), etc.

Props are fairly easy to understand, especially if you are coming from another framework like React or Vue, so I won't cover them in detail here (just use the Svelte docs, they are great!). However, context and store are a little more advanced.

Context vs Store

There are quite a few differences between context and store, although at a first glance they might not be clear. I've just started learning Svelte, so take this with a grain of salt, but I think this is a fair comparison:

👇 Context Store
Observable? No Yes
Reactive? No* Yes
Can exchange data with ___ components Data is sent from parent, downwards
(must be in same tree)
Any!

Summarizing another way, the main use of context is to share data downwards, in a component tree. Since it is not reactive, often it is used to pass something that does not change, such as a theme object set by a parent component.

On the other hand, store can be used to share data across your entire app (even outside of components!), and you can use it reactively to have components respond to changes, or observe mutations. Although the data being global is usually a benefit, sometimes context might be desirable if you want data isolated to a specific component tree.

* = 💡 Another way to use context and store is actually to use them... together! By passing a store object through context, it can be shared among a component tree by a parent and provide reactivity to child components!

💡 Don't forget that you can prefix store variables with $ to use them reactively without needing to set up a subscription / observer. You can even use this feature with input bindings or on: event bindings!

If you are looking for more info on context and store, here are some more resources:

Reloading and Hot Module Replacement (HMR)

To understand the difference between full reloads and Hot Module Replacement (HMR), I have a short explainer on my JS Bundler page.

The short version is that HMR only reloads the part of your app that has actually changed, and leaves everything else alone; this often means that app state is preserved, whereas in a full reload it is always destroyed.

Svelte offers live reloading (refreshing the entire webpage after a change) out-of-the-box with the official template, but live hot reloading (via Hot Module Replacement, aka HMR), is an entirely different matter.

Here are the options to get HMR working with Svelte:

HMR with TypeScript

Using TypeScript with HMR is another layer of complexity on top of systems that are already kind of tricky. The best places to look for more info are:

Due to the complexities of setting up TypeScript + HMR + Dev Server + Bundling, most users might prefer to stick with Vite or Svite, both of which reduce boilerplate configs and streamline the whole process into one wrapper tool. I would recommend Svite if you are looking for the easiest way to quickly get HMR + bundling setup, with minimal configuration (example).

Vite now has an "out-of-the-box" / "create-app" template for Svelte + TypeScript! You can use npm init @vitejs/app my-svelte-app --template svelte-ts, see docs for details.

Misc / How Do I...

  • Pass through arbitrary attributes on a custom component, from parent to child?

    • You could use {...$$restProps} in the child to spread the props received, but not declared with export (however, this is recommended against, for optimization reasons)
  • Pass through class names?

  • Use CSS variables in style sections?

    • Go for it! You can totally use CSS variables in style blocks. But... keep in mind that Svelte's "scoping" will not magically scope your variables if you do something like :root { --myVar: ... }
    • It is often safer to dynamically build the CSS and deliver it through an inline style="" attribute

      • You can even inline CSS variable declarations!
  • Have an HTML attribute only output in the DOM if the JS variable being assigned to it is == true (e.i. do not output my-attribute="false")

    • For example, for data-selected={selected}, you might want the data-selected to only show up in the DOM if selected === true - if it is false, the attribute should just not be there. However, this is not the default behavior of Svelte, unless the attribute you are using is A) a native HTML attribute it knows about and B) it knows is a boolean attribute
    • The docs call this out; you need to use a nullish instead of a falsy value if you want the attribute to not show

      • Easy approach: my-attr={myVar || null} (or my-attr={!!myVar || null}, if you want to force boolean coercion).
  • How to pass methods up through the component tree? (aka expose and call a function in a child component)

    • Best practice is to export the method in the child via export function myFunction(){} or export const myFunc = () => {}, and then use bind:this={variableToExportsFromChild} in the parent (component binding)

      • See stackoverflow.com/a/61334528/
      • ⚠ You will not be able to access the component variable until the component is mounted / rendered, so you might want to wrap in onMount() if trying to use immediately.
    • You could use dispatched events as an alternative, although that requires more boilerplate. However, dispatched events can also travel downwards in addition to upwards.
    • If you are just trying to pass data upwards, and not actual methods, you can always use prop binding in the parent
  • How to make a prop optional

    • Assign a default value when exporting it (e.g. export let myProp = 5)
  • How to accept and render an array of elements via a prop

  • How to move components between places (e.g. from one parent to another), with animation in-between?

    • Svelte actually makes this so easy compared to a lot of other systems! You can use the send and receive transitions, combined with the crossfade function
    • Details:

  • Use static assets (images, svgs, etc.)?

    • One way is to simply place those assets in /public, and then reference them via relative (or absolute without domain) paths / URLs

      • This will not catch typos, or optimize unused assets out of production
    • Another way is to use the processing power of Rollup, which is being used as the default bundler anyways. You can use any number of plugins to accomplish various import tasks, such as letting you import svg files as strings to use directly in code

      • See github.com/rollup/plugins
      • This is better than placing in /public, because Rollup can make sure only imported assets are included in the final bundle, and it should also catch typos / incorrect imports
      • For more advanced parts, see separate section below: "Working with Images"
  • How to directly inject HTML strings, equivalent to React's dangerouslySetInnerHTML, etc.

    • Instead of {myHtmlStr}, use {@html myHtmlStr}
    • See Docs: "HTML Tags"
  • Extend classes, use base template component, reuse component structure, etc.

    • The easiest way is to usually have your base component use the special slot API to accept children, and then using events, exported methods, etc., to pass the necessary things back up

General Troubleshooting

  • UI is not updating after an array or object property is updated, which is used in template

    • Svelte's reactive binding is based on assignments; modifying arrays (or object properties) in-place will not trigger updates

  • My UI is not updating / re-rendering, when it should be!

    • For arrays and object properties, see above (those are special, since Svelte's reactivity is based on assignment)
    • Other than making sure that variables are getting updated by assignment, other things to look for are:

      • Incorrectly setting up computed properties

        • If you have a computed value that you want refactored into a top-level function (e.g. getImagePath), when using it inside the template having something like <img src={getImagePath()} /> will actually result in stale values if props change.
        • The correct way is usually to use reactive declarations, with the $: prefix syntax. For a complicated computed value, you can use braces to replace a function
  • My on:{evt} is not working on a custom component!

    • To attach event bindings to a custom component, you must use event forwarding
    • For on:click, and other DOM events, this is as easy as putting an empty on:{evt} on the element you want to forward from in the child (demo)
  • My component binding is not working (e.g. bind:this={component_instance})

    • component_instance will be undefined until the component is mounted / rendered. You can wrap with the onMount() lifecycle hook if you are trying to access during initialization
  • Transitions that rely on coordinate position changes (e.g. transition:fly) don't seem to be working correctly

    • Some of the transitions can clash with various combinations of CSS positioning declared outside the transition. Instead of using a single transition:{transitionAnimation}, try switching to explicitly declaring each part (in:{transitionAnimation}, out:{transitionAnimation}), as well as explicitly declaring the in and out coordinates (x and y)

VSCode / IDE Support

  • Prettier is not playing nice with my .svelte files!

    • If you are using the recommended VSCode extension (Svelte for VSCode), try putting your prettier config in .prettierrc, instead of any other method (for example, in .vscode/settings.json)

      • You can use svelteSortOrder to control tag order
    • There is also a dedicated Prettier plugin - prettier-plugin-svelte

      • This comes bundled with the language server / VSCode plugin
    • Here is how the VSCode extension resolves Prettier formatting
    • After making or updating .prettierrc file, run Svelte: restart language server)
  • Props are not being hinted correctly / intellisense is not working

TypeScript

Make sure you follow the official guide on how to get setup with TS (mainly just running the TypeScript setup scripts / ejector).

You will probably also want to install and use @tsconfig/svelte

📘 Also important reading: Language Tools: TypeScript Support

TypeScript - Exporting and Importing Types from a Svelte SFC

Rather than using a shared type file (e.g. types.d.ts, or something like that), you might want to export a type that is created inside of a Svelte SFC (.svelte file).

If you try this within the normal script block, you will get this error:

Modifiers cannot appear here. If this is a declare statement, move it into <script context="module">..</script> ts(1184)

Here is how to use the context=module block:

BlueOrRedButton.svelte:

<script lang="ts" context="module">
	export type ColorOption = 'blue' | 'red';
</script>

<script lang="ts">
	export let color: ColorOption;
</script>

<button style="background-color: {color};"> Click Me! </button>

For our component that imports the above component and its type, normally you could combine both imports into one statement, like:

import RedOrBlueButton, { ColorOption } from './RedOrBlueButton.svelte';

Unfortunately, the above might cause this error:

Uncaught SyntaxError: The requested module '/src/RedOrBlueButton.svelte?import' does not provide an export named 'ColorOption'

And if you try to separate out the imports, you might get a new error: This import is never used as a value and must use 'import type' because 'importsNotUsedAsValues' is set to 'error'.. The least error-prone approach appears to be:

import type { ColorOption } from './RedOrBlueButton.svelte';
import RedOrBlueButton from './RedOrBlueButton.svelte';

TypeScript - Sharing Prop Types

TypeScript - Prop Types - via Common Shared Type Files

If you are OK with declaring your types outside of the SFC where they are being used, then a common shared type file is fairly manageable approach. You could setup ambient types (e.g. *.d.ts), which require no explicit imports and are magically available anywhere (but clutter the global type system), or use explicit import and export statements.

TypeScript - Extracting Prop Types via $$prop_def

Rather than exporting interfaces, and/or using global type declarations, there is actually a way to extract the types of a components props when you import it, for the purposes of static type checking. You can use the exposed $$prop_def property on the imported component instance.

For example, assume we have FancyButton.svelte, which has properties of icon, confirmText and palette, which we want to re-use within our wrapping component that is importing it, DeleteButton.svelte:

DeleteButton.svelte:

<script lang="ts">
	import FancyButton from './FancyButton.svelte';

	// We can reuse a prop of FancyButton as our *own* prop!
	export let icon: FancyButton['$$prop_def']['icon'];

	// We could also extract and use internally, or really do anything that we
	// can do with regular types
	type ConfirmTextWithFontStyle = FancyButton['$$prop_def']['confirmText'] & {
		fontStyle: CSSStyleDeclaration['fontStyle'];
	};

	// Exposing our combined type as a new prop
	export let deleteConfirmText: ConfirmTextWithFontStyle;
</script>

<FancyButton {icon} style="font-style: {deleteConfirmText.fontStyle};" confirmText={deleteConfirmText} />

Note: This relies on the internals of Svelte language tools, and should be used for type-checking only. This approach is also not the best for every situation.

TypeScript - Explicit Importing / Exporting of Prop Types

As an alternative to using $$prop_def, or a shared common types file, you can also export non-properties in a Svelte SFC, by using the <script context="module"> block.

In a TypeScript SFC, for sharing a prop type, that might look something like:

LaptopDisplay.svelte:

<script lang="ts" context="module">
	export interface Laptop {
		year: number;
		model: string;
		brand: string;
	}
</script>

<script lang="ts">
	export let laptop: Laptop;
</script>

<p>
	You have selected a {laptop.model},
	by {laptop.brand},
	manufactured in {laptop.year}.
</p>

For how to import the type, and how to use separate type imports / exports in general with Svelte SFCs, see the section above: "Exporting and Importing Types from a Svelte SFC".

TypeScript - Typing Custom Transition Functions

At the time of writing this, my preferred way of typing a custom transition function is like so:

import type { TransitionConfig } from "svelte/transition";

const myTransition = (node: HTMLElement, params: TransitionConfig): TransitionConfig => {
	return {
		...params,
		css: (t: number, u: number) => {
			// return CSS string
		}
		// or,
		// tick: (t: number, u: number) => { // do something }
	}
}

Make sure to review the docs: "Custom transition functions"

TypeScript - Interfaces as Attributes

If a component has a lot of different attributes, and those attributes are shared across a common interface, you might want to make your code more succinct by adopting a interface type and reusing it:

// Shared interface
interface ChatMessage {
	sender: string;
	receiver: string;
	sentAt: number;
	readAt?: number;
	message: string;
}
<script lang="ts">
	import { ChatMessage } from './types.ts';

	export let sender: ChatMessage['sender'];
	export let receiver: ChatMessage['receiver'];
	export let sentAt: ChatMessage['sentAt'];
	export let readAt: ChatMessage['readAt'];
	export let message: ChatMessage['message'];
</script>

However, as you can see above, this still involves a decent amount of repetitive code.

One easy and quick way to avoid all this boilerplate is to export an attribute that accepts the entire interface as an object, instead of breaking each property out by itself:

<script lang="ts">
	import { ChatMessage } from './types.ts';

	export let messageObj: ChatMessage;
	// You could optionally destructure immediately for easier use inside the SFC
	// E.g. `const {sender, message} = messageObj;`
</script>

However, if you still want your component to have separated out attributes while reusing the shared interface, it gets a little tricker. For example consider the following:

<script lang="ts">
	import { ChatMessage } from './types.ts';

	export let {sender, receiver, sentAt, readAt, message}: ChatMessage = {} as ChatMessage;
</script>

On the surface, this seems to get us close, but unfortunately has two main issues

  • Error: ValidationError: Cannot declare props in destructured declaration
  • it makes every attribute accept undefined as an option, turning sender: string into sender?: string | undefined and making every attribute optional. This has to do with how Svelte treats default prop / attribute values.

I'm not sure if there is a current way to get around these issues, but I'll update this if I find one.

TypeScript - Issues

💡 Tip: When something simple seems like it should be working, and it isn't, try running Svelte: restart language server. This often fixes things

  • Error: Type annotations can only be used in TypeScript files.

    • Make sure you don't have an explicit file association for *.svelte files in any (local or global) vscode settings.json files.

      • If you find one, remove it, then reload the entire window
    • Try restarting the language sever with the cmd: Svelte: restart language server
  • Svelte files not picking up global types (e.g. declared through ambient *.d.ts files)

    • If the file was just created, try restarting the language server
    • Make sure you have actually saved updates to the file, not just modified it. Unlike VSCode's JS/TS type checking system, which will use "dirty" files (unsaved modifications), the Svelte extension requires the file to be saved.
  • Error: (plugin typescript) Error: Could not load ___.ts (imported by ___.svelte): Debug Failure. False expression: Expected fileName to be present in command line

    • This is actually an error from rollup, which is the default bundler shipped with Svelte. Usually this means that the file you are trying to reference was not noticed by the "watcher" (tends to happen with brand new files that were created after the watcher was started)
    • If you are running in watch mode, just restart (kill & then run dev)
  • Error: JSX element type '___' does not have any construct or call signatures.

    • Did you miss providing a default export ___ which exports the SvelteComponent?
Markdown Source Last Updated:
Mon May 24 2021 09:40:37 GMT+0000 (Coordinated Universal Time)
Markdown Source Created:
Wed Sep 30 2020 03:51:18 GMT+0000 (Coordinated Universal Time)
© 2021 Joshua Tzucker, Built with Gatsby
Feedback