to learn by doing. Following https://www.youtube.com/watch?v=8DQailPy3q8
Note that all the below notes are transcribing, paraphrasing or reorganizing information from the above tutorial.
No library code shipping - Svelte has less framework code.
It does much less at the library level (compared to react) and much more after the compiling into js.
It's like html with javascript sprinkled into it:
<script></script>
tag is where imports, javascript, and states are setup, etc..<style>
button { background: red; }
</style>
A variable defined with $state()
will cause an update wherever it is used
In react, when a state variable changes and causes a rerender the whole component function reruns.
In Svelte, values are tracked: the template tag (eg {count}
inside <button></button>
) becomes a compiled reactive variable.
a variable that becomes html - it's not components that are rerendering it's values.
npx sv create
uses svelte kit. it is the app framework for svelte. you don't need svelt kit to write svelte sites but its easiest way to get up and running.
we won't be doing many svelte kit specific things other than use the basic structure.
lang="ts"
in script tag for the file to setup typescript support
$props()
:usually when you pass in a variable from somewhere into a component. done with propName = {propValue}
in the tag of component:
<Component propName = "propValue" />
and accessed from the component file through:
<script>
let { propName } = $props();
</script>
and passed into html like:
<h1> Hello {propName} </h1>
it can also take javascript expression just like JSX:
<h1> Hello {propName == 'John'} </h1> //returns true or false and prints that
Note: the dollar sign in $props()
indicates it's a 'rune', which is a svelte language feature (indicates something svelte specific and important going on there)
semi colon after destructuring the props and nest prop label and type in curly brackets (usual Ts syntax - note it's not an object)
<script>
let { name , clickFunc } : {
name: string;
clickFunc: event(MouseEvent) => void; //if passed in to 'onclick'
} = $props();
</script>
with Typescript:
let {
name,
optional_prop
}: {
name : string;
optional_prop?: string //the question mark tells Ts it might not appear in props
} = $props();
without Typescript:
let {
name,
optional_prop = null; //default value
} = $props()
$state
, $derived
variable wise, one can do:
let name2 = "Scott";
and use that in the html (eg <h2>{name2}<h2>
)
$state
But in svelte 5, the proper way to do this is to use the rune $state() to make this variable reactive:
let name2 = $state('Scott');
$derived
If you have a value that depends on another.
eg shopping cart - there are items, as well as a total.
the total sum depends on the items.
this can look like:
let cart = $state(
[
{item:'potato', price: 3},
{item: 'eggs', price: 4}]
);
let total = $derived({
cart.reduce(sum,item) => sum + item.price
})
so in a way derived will store a transformation and the value that comes out of it.
<input>
to a variableit's possible to very directly bind the value of an input field to a state variable using bind:value
:
<input type="text" bind:value={name2} />
and every subsequent component that takes this variable as a prop will update too (no need for onChange function)
--> can also bind a value to an html element! so one can access the element as a variable! a lot more there
A way to remain organized with states and functions - like with declaring classes that have their own method - is possible. let's say we want to create a way to get a value, increment it, and set it to a certain number, all within what's allowed in Svelte:
export function createState() {
let value = $state(0); //initialize as 0
function increment() { //function definition
value ++;
}
return {
get value() { //svelte thing to get most recent value of state. simple method.
return value;
}
set value(newVal) { //a way to allow manually setting value
value = newVal;
}
increment, //expose the function (make it a method)
}
}
now import this to a component and begin using it:
<script>
import {createState} from './fileName.svelte';
const myState = createState();
//this now gives us access to doing things to myState:
</script>
<h1>{myState.value}<h1> //the get function
<button onclick={myState.increment}>Increment</button> //the increment function
<button onclick={() => {myState.value = 10} }>set Val to 10</button> //the set function allows this
This all allows ways to interact with a state.
This is similar to above but a class so it is neater to write and allows more:
export class classState {
value = $state(0); //creates it
increment() { //method
this.value += 1; //can use 'this'
}
}
Though authoring this is much neater, one small disadvantage is to have to bind it to onclick differently:
<script>
import {classState} from './fileName.svelte';
const myClassState = new classState();
</script>
<button onclick={
() => myClassState.increment();
}
> increment </button>
onclick
events<script>
let status = $state('OPEN');
function toggleStatus() {
status = status==='OPEN' ? 'CLOSED' : 'OPEN';
}
</script>
<p>the store is now {status}</p>
<button onclick={toggleStatus}>Toggle status</button>
the onclick
property of a <button>
(html) takes in the function definition (on react it's onClick, in old svelt it was on:click)
outside the script tags - in between the html tags - one can use {#if}
to begin a section of conditionally rendered html and {/if}
to close it. can also add an {:else}
:
{#if count > 0 }
<p>Count no longer empty</p>
{:else}
<p>Count is empty, add something<p>
<button onclick={()=> count+= 1}></button>
{/if}
can also do {:else if xyz === 1 }
(chain conditional logic)
Note: the backslash in the curly brackets {/}
is common in Svelte - it's similar to how html has closing tags but for Svelte.
{#each}{/each}
The #each
in svelte replaces what we usually do with a for loop, map or for each.
e.g. if we have an array:
const Questions = [ //defined in script tag
{text: 'question1', id: '3'},
{text: 'question2', id: '4'},
{text: 'question3', id: '6'},
]
and want to loop through it for rendering html, we can do
{#each arrayName as arrayValue}
and choose what to do for each value:
{#each Questions as question (question.id) } //each loop opening tag
{question.id} //will render id string
{@render formStep({question.text})}
{/each}
//^remember to close it
Note the (question.id) is a keyblock that helps the renderer but is not necessary.
can also destructure the array value properties:
{#each Questions as {text, id} (id) }
{id} //will reference what used to be question.id
{@render formStep({text})}
{/each}
useEffect()
)effects or side-effects just mean "this code runs when something else happens".
slightly more verbose way: "When some data changed or a component is mounted etc, this code will run."
in Svelte 5, we use $effect()
which takes in a function:
$effect(() => { //by default will run onMount once
console.log('on mounted');
})
$effect(() => { //by default will run onMount once
console.log('on mounted');
return () => {
console.log('post change');
}
})
//will rerun when formState.step has changed
$effect( () => { //each time the formStep is run
console.log('formstate', formState.step);
})
This is different from react where the dependency needs to be explicitly placed in another parameter at the end.
and this will happen before it reruns:
//will rerun when formState.step has changed
$effect( () => {
return () => { //before it reruns
console.log('before formstate reruns', formState.step); //returns old step
}
})
Warning: DON'T create state based off other state inside $effect
. Simply use $derived
instead.
$inspect
use inspect to check on variables in the console - like Svelte's console log, will log whenever an initialization or change happens, eg:
$inspect(formState.step);
//logs 'init 0', then 'update 1' when step is incremented
A snippet allows to define a reusable chunk of code inside a Svelte template
#snippet
here a snippet is defined:
{#snippet formStep({ question, id, type} : {question: string; id: string; type: string})}
<article>
<div>
<label for={id}>{question}</label>
<input {type} {id} bind:value={formState[id]}
/>
</div>
</article>
{/snippet}
It can now be called in the html section just like a function (eg how react works). However it needs to be run with {@render snippetName}
@render
In the section of the Svelte file that is rendering html, the snippet can be rendered with @render
:
<main>
{@render formStep({
question: "What's your name",
id: 'name',
type: 'text'
})}
</main>
Note the snippet will have access to all variables within the scope of this component and doesn't need its own props.
@render
://1_it's possible to use a component to wrap other things
<Header name={formState.name}>
<div>
<p>I'm the Header's child</p>
</div>
</Header>
//2_ What is wrapped is now accessible through the 'children' prop inside the component file:
<script lang='ts'>
import type { Snippet } from 'svelte';
let {
name,
children //contains whatever was between <Header></Header>
} : {
name: string;
children: Snippet
} = $props();
</script>
//now render children with file html
<div>
{@render children()} //here
</div>
Important note: powerful because children passed in this way can pass information upwards, like what prop drilling achieves. eg if one of the children is a snippet or function that uses variables from parent component.Global styling still works (more later). But the powerful additional that Svelte adds is that the <style></style>
tags in a single file component targets the styles in that file only (not parents or children). This helps with scoping styling locally.
eg:
<p class="error">Error message</p>
<style>
p {
color: red;
}
</style>
The way it works is Svelte will create a class just for this element behind the scenes eg '.p.svelte-25onta1
'
to have styling affect elements outside the component, we can use the :global()
selector function:
<style>
:global(p) {
color: red;
}
</style>
now any paragraph will receive this style
Can use transition:
by setting properties to html elements, eg:
<div transition:fly={{ x: 200, duration: 200, opacity: 0}}>
//need to import: import 'Fly' from 'svelte/transition';
Can also do in:
and out:
:
<div in:fly = {{ x: 200, duration: 200, opacity: 0, delay: 200}}
out:fly = {{ x: -200,duration: 200, opacity: 0}}
>
There are other transitions in addition to fly
, like fade
, blur
, slide
, scale
, draw
and crossfade
.
There is a lot more animation things like action, motion, etc.
See eg https://svelte.dev/docs/svelte/svelte-motion