2020-06-20
Svelte tutorial note
svelte, javascript, selfnote
svelte, javascript, selfnote
Image by William Krause from Unsplash
This is a note as I wrote down as I was going through Svelte tutorial.
Might be of helpful for some but foremost, this is a note for myself :)
https://svelte.dev/blog/svelte-for-new-developers
npx degit sveltejs/template new-project-name
Install following extensions
https://svelte.dev/tutorial/reactive-assignments
on:click on looks like a directive and click is the event name.
States are reactive, closure under script tag re-renders whenever the state value changes.
1<script>2 let count = 0;3
4 function handleClick() {5 count++;6 }7</script>8
9<button on:click="{handleClick}">Clicked {count} {count === 1 ? 'time' : 'times'}</button>
https://svelte.dev/tutorial/reactive-declarations
Computed/derived states need to be declared using a special syntax, $:.
1let count = 0;2$: doubled = count * 2;
Useful when it needs to be access multiple times.
Instead of {count * 2} everywhere, you can use {doubled} instead.
https://svelte.dev/tutorial/reactive-statements
$: isn't limited to expressions (reactive values) but also to statements.
1<script>2 let count = 0;3
4 $: console.log(`the count is ${count}`);5
6 $: if (count >= 10) {7 alert(`count is too high!`);8 count = 9;9 }10
11 function handleClick() {12 count += 1;13 }14</script>15
16<button on:click="{handleClick}">Clicked {count} {count === 1 ? 'time' : 'times'}</button>
https://svelte.dev/tutorial/updating-arrays-and-objects
A simple rule of thumb: the name of the updated variable must appear on the left hand side of the assignment.
Or assign a new reference like you do in React.
1// Instead of this2function addNumber() {3 numbers.push(numbers.length + 1);4 numbers = numbers;5}6
7// Do this8function addNumber() {9 numbers = [...numbers, numbers.length + 1];10}
https://svelte.dev/tutorial/declaring-props
For passing data to another component(s). Same concept as it does in React.
In React, components receive props but in Svelte, you export a variable.
Nested.svelte
1<script>2 export let answer;3</script>4<p>The answer is {answer}</p>
App.svelte imports Nested component and passes the answer like following.
1<script>2 import Nested from "./Nested.svelte";3</script>4
5<Nested answer="{42}"></Nested>
https://svelte.dev/tutorial/default-values
You can set the default prop value during declaration
Nested.svelte
1<script>2 export let answer = "is unknown!";3</script>4<p>The answer is {answer}</p>
If no props passed to Nested like <Nested>, then the default value is used.
https://svelte.dev/tutorial/spread-props
As you can do in React, you can pass multiple props with object spread operator.
<Info {...pkg}></Info>
https://svelte.dev/tutorial/if-blocks
The markup is avaiable in Svelte only, not in HTML.
1{#if user.loggedIn}2<button on:click="{toggle}">Log Out</button>3{/if} {#if !user.loggedIn}4<button on:click="{toggle}">Log In</button>5{/if}
https://svelte.dev/tutorial/else-blocks
Mutually exclusive condition can use {:else} block
1{#if user.loggedIn}2<button on:click="{toggle}">Log Out</button>3{:else}4<button on:click="{toggle}">Log In</button>5{/if}
https://svelte.dev/tutorial/else-if-blocks
Additional condition can be checked with {:else if condition}
1{#if x > 10}2<p>{x} is greater than 10!</p>3{:else if x < 5 }4<p>{x} is less than 5 {:else}</p>5
6<p>{x} is 'teween 5 and 10</p>7{/if}
https://svelte.dev/tutorial/each-blocks
You can iterate an iterable object with {#each iterable as alias, index}
1<ul>2 {#each cats as cat, index}3 <li>{index + 1}th cat is {cat.name}</li>4 {/each}5</ul>
The alias can be destructured like
1{#each cats as {name, id, age}, index}2<li>{index + 1}th cat is {name} and is {age} years old</li>3{/each}
https://svelte.dev/tutorial/keyed-each-blocks
In React, creating an iterable element requires key per each element.
e.g.)
1{2 things.map(thing => <li key={thing.id}>{thing.color}</li>);3}
In Svelte, you specify the key in the markup.
1{#each things as thing (thing.id)}2<li>{thing.color}</li>3{/each}
Or you can destructure thing
1{#each things as {id, color} (id)}2<Thing current="{color}" />3{/each}
https://svelte.dev/tutorial/await-blocks
Svelte markup has a way to await promises.
Race condition is handled automatically because Svelte only grabs the latest/most recent promise only.
1{#await promise}2<p>Loading...</p>3{:then number}4<p>The value is {number}</p>5<p>{:catch error}</p>6
7<p class="error">{error.message}</p>8{/await}
You can decide not to show the intermediate "loading" message and wait 'til the promise resolves.
1{#await promise then number}2<p>The value is {number}</p>3<p>{/await}</p>
This is much cleaner abstraction than in React, in which one needs to use useEffect to resolve promise in an async method and set the state.
https://svelte.dev/tutorial/dom-events
Use on: directive, followed by DOM event name.
e.g.) mousemove
1<script>2 let m = { x: 0, y: 0 };3
4 function handleMousemove(event) {5 m.x = event.clientX;6 m.y = event.clientY;7 }8</script>9
10<style>11 div {12 width: 100%;13 height: 100%;14 }15</style>16
17<div on:mousemove="{handleMousemove}">The mouse position is {m.x} x {m.y}</div>
https://svelte.dev/tutorial/inline-handlers
โ Inline event handlers does not cause any performance issues unlike in React, as Svelte knows how to optimize.
Instead of,
<div on:mousemove="{handleMousemove}">The mouse position is {m.x} x {m.y}</div>
You can inline handleMousemove as shown below.
1<div on:mousemove={e => m = {x: e.clientX, y: e.clientY}}>2 The mouse position is {m.x} x {m.y}3</div>
Or, wrap the inline method in quotes for syntax highlighting in some editors
1<div on:mousemove="{e => m = {x: e.clientX, y: e.clientY}}">2 The mouse position is {m.x} x {m.y}3</div>
https://svelte.dev/tutorial/event-modifiers
You can "decorate" (my intepretaion) event with modifiers such as
e.g.) using once to run an event handler only once on a button
<button on:click|once="{handleClick}">Click me</button>
Modifiers are chainable. on:click|once|capture|preventDefault
handleClick will be called once once no matter how many times you press the button.
โ Space is significant! The code below is not valid as there are spaces between |.
<button on:click | once="{handleClick}">Click me</button>
https://svelte.dev/tutorial/component-events
Unlike custom event dispatch in vanilla JS, where you pass custom data as detail property,
1// add an appropriate event listener2obj.addEventListener("cat", function(e) { process(e.detail) });3
4// create and dispatch the event5let event = new CustomEvent("cat", {6 ๐7 detail: {8 hazcheeseburger: true9 }10});11obj.dispatchEvent(event);
you dispatch an event with data and it will be available as part of event.detail automatically.
Inner.svelte
1<script>2 import { createEventDispatcher } from "svelte";3
4 const dispatch = createEventDispatcher();5
6 function sayHello() {7 // NOT THIS!8 // dispatch('message', {detail: {text: 'hi!'}})9 // But pass the data as it is10 dispatch("message", { text: "Hello!" });11 }12</script>13
14<button on:click="{sayHello}">Click to say hello</button>
You can then use the component and subscribe to the event, message like following.
App.svelte
1<script>2 import Inner from "./Inner.svelte";3
4 function handleMessage(event) {5 // Access "text" via ๐ event.detail6 alert(event.detail.text);7 }8</script>9
10<Inner on:message="{handleMessage}" />
This pattern is different from React where an inner component receives an "event handler" as a function and calls it, not declare an event.
1const App = () => <Inner onMessage={handleMessage}>2const Inner = ({onMessage}) => <button onClick={onMessage}>Click</button>
So it seems that in Svelte, event handlers are declared using vanilla JavaScript's CustomEvent interface.
https://svelte.dev/tutorial/event-forwarding
DOM events are bubbled up while Svelte events aren't. Explicit event forwarding can be done by creating event dispatcher in each level.
Svelte can forward events with a shortcut where you specify the on:eventname directive without a value.
<Inner on:message></Inner>
Then all on:message event handlers will be forwarded up and made available to the calling component.
Note: this is tough to grasp. Need to come back later.
https://svelte.dev/tutorial/dom-event-forwarding
Svelte requires you explicitly decide whether to expose an event or not.
When there is more than one element in inner component exposing the same event, say two buttons with on:click,
CustomButton.svelte
1<button id="b1" on:click>Click me</button>2
3<button id="b2" on:click>Click me2</button>
Then you can tell which one was fired by examining event.target
App.svelte
1<script>2 import CustomButton from "./CustomButton.svelte";3
4 function handleClick(event) {5 console.log(`e =>`, event.target);6 }7</script>8
9<CustomButton on:click="{handleClick}"> /></CustomButton>
CustomButton click on #b1 and #b2 results in,
1e => <button id=โ"b1">โClick meโ</button>โ2e => <button id=โ"b2">โClick me2โ</button>โ
https://svelte.dev/tutorial/text-inputs
Sorta like a two-way binding, where changes in an element updates the state and the current state.
1<script>2 let name = "world!";3</script>4
5<input bind:value="{name}" />6
7<h1>Hello {name}!</h1>
Updating values in input will update name state as well as the input's value.
https://svelte.dev/tutorial/numeric-inputs
batteries included
Svelte auto converts input of type number & range to numeric values.
React requires explicit conversion as it's metal.
https://svelte.dev/tutorial/checkbox-inputs
Checkbox input type value is bound to bind:checked instead of bind:value.
1<script>2 let isChecked = false;3</script>4<input type="checkbox" bind:checked="{isChecked}" />
https://svelte.dev/tutorial/group-inputs
In vanilla JS, you use name to group related checkboxes and radio.
MDN Reference: <input type="radio">
1<form>2 ๐3 <input type="radio" name="scoops" value="1" />4 <input type="radio" name="scoops" value="2" />5 <input type="radio" name="scoops" value="3" />6</form>
but in Svelte, you bind a group using bind:group directive.
1<form>2 ๐3 <input type="radio" bind:group="scoops" value="1" />4 <input type="radio" bind:group="scoops" value="2" />5 <input type="radio" bind:group="scoops" value="3" />6</form>
When bound to a radio group, then the bound value is one value, but on checkboxes, the bound value is an array.
1<script>2 let scoops = 1;3 let flavours = [];4</script>5
6<!-- Radio `scopes` bound to a single value -->7<label>8 <input type="radio" bind:group="{scoops}" value="{1}" />9 One scoop10</label>11<label>12 <input type="radio" bind:group="{scoops}" value="{2}" />13 Two scoops14</label>15<label>16 <input type="radio" bind:group="{scoops}" value="{3}" />17 Three scoops18</label>19
20<!-- Checkbox group value, `favlours` is an array -->21<label>22 <input type="checkbox" bind:group="{flavours}" value="Cookies and cream" />23 Cookies and cream24</label>25<label>26 <input type="checkbox" bind:group="{flavours}" value="Mint choc chip" />27 Mint choc chip28</label>29<label>30 <input type="checkbox" bind:group="{flavours}" value="Raspberry ripple" />31 Raspberry ripple32</label>
https://svelte.dev/tutorial/textarea-inputs
Same as <input type="text">. You bind value using bind:value={value}. If the value variable name matches value, then you can leave out the assignment, like,
<textarea bind:value></textarea>
https://svelte.dev/tutorial/select-bindings
Like Textarea, you can use bind:value={value} and leave out the assignment, bind:value if the variable name is value.
1<script>2let value;3let answer = ""4const questions = [5 {id: 1, 'question #1'},6 {id: 2, 'question #2'},7 {id: 3, 'question #3'},8]9</script>10
11<!-- this works too ๐ -->12<!-- <select bind:value={value} on:change="{() => answer = ""}"> -->13<select bind:value on:change="{() => answer = ""}">14 {#each questions as question}15 <option value={question}>{question.text}</option>16 {/each}17</select>18
19<input bind:value={answer}>
https://svelte.dev/tutorial/multiple-select-bindings
I've already mentioned this in d. Group inputs - https://svelte.dev/tutorial/group-inputs
Binding to a select with multiple directive sets the value to an array.
flavours is an array.
1<select multiple bind:value="{flavours}">2 {#each menu as flavour}3 <option value="{flavour}">{flavour}</option>4 {/each}5</select>
https://svelte.dev/tutorial/contenteditable-bindings
You can bind to either textContent or innerHTML
1<div contenteditable="true" bind:innerHTML="{html}"></div>2<!-- or -->3<div contenteditable="true" bind:textContent="{html}"></div>
Check out the difference between textContent & innerHTML
& why one should consider using textContent over innerHTML.
https://svelte.dev/tutorial/each-block-bindings
Don't use this if you plan to go with immutable data (React style).
Familiar with imperative style? go with this.
https://svelte.dev/tutorial/media-elements
Media elements' (video/audio) are updated more frequently using requestAnimationFrame.
https://svelte.dev/tutorial/dimensions
Every block-level elements, such as div, section, article, etc have bindings to following props.
https://svelte.dev/tutorial/bind-this
bind:this={variable} returns a reference to rendered elements.
variable will be undefined until the component has mounted.
Use onMount lifecycle to refer to the variable.
Note: This looks like ref in React.
https://svelte.dev/tutorial/component-bindings
As mentioned previously, you can bind:value for custom components to provide a two-way binding.
Changes in child component will be available in the parent element.
Keypad.svelte
1<script>2 export let value;3</script>4...
Suppose that in App.svelte,
1<script>2 import Keypad from "./Keypad.svelte";3
4 let pin;5
6 const handleSubmit = () => console.log(`pin => ${pin}`);7</script>8
9<input bind:value="{pin}" />10<Keypad bind:value="{pin}" on:submit="{handleSubmit}"></Keypad>
You can bind to Keypad with bind:value={pin}. It works as both an input and output to Keypad component.
It can be demo'ed by changing values in <input bind:value={pin} />.
Awesome! Very convinient. But you have to be careful as you can lose track of the state flow.
In React, one would have to pass a callback function to call it whenever child value changes and the parent would update the state via the callback.
App.jsx
function App() { const [pin, setPin] = React.useState(null) return <Keypad onChange="{setPin}" /> }
https://svelte.dev/tutorial/onmount
It's comparable to the mix of componentDidMount and useEffect because it's called when a component is mounted, and cleans up with a callback function returned from it (that's how useEffect does a clean up).
And also, componentDidMount can be async and useEffect cannot call an async method.
As it's the recommended way to call fetch in React, onMount is normally where one should make a network request.
1<script>2 import { onMount } from "svelte";3
4 onMount(async () => {5 const response = await fetch("https://www...");6 photos = await response.json();7
8 return () => {9 // clean up resources here10 };11 });12</script>
https://svelte.dev/tutorial/ondestroy
onDestroy is like React's componentWillUnmount. Use it clean up resources on component's unmount phase.
1<script>2 import { onDestroy } from "svelte";3
4 let seconds = 1;5 const id = setInterval(() => seconds++, 1000);6
7 onDestroy(() => void clearInterval(id));8</script>
https://svelte.dev/tutorial/update
Flows like,
beforeUpdate -> onMount -> beforeUpdate -> state changes -> afterUpdate -> onDestroy
As beforeUpdate runs BEFORE onMount, one needs to check for existence of elements
https://svelte.dev/tutorial/tick
To get around batch processing (state updates, DOM updates, etc)
1<script>2 import { tick } from "svelte";3</script>
https://svelte.dev/tutorial/writable-stores
Svelte has batteries included. It comes with a global state management library.
svelte/store has writable method to create a global state.
store.js
1import { writable } from "svelte/store";2
3export const count = writable(0);
Then one can import count in store.js, either to read, update, or set the value.
1<script>2 import { onDestroy } from "svelte";3 import { count } from "./store";4
5 let countValue;6 const unsubscribe = count.subscribe(value => {7 countValue = value;8 });9 // Clean up after your business!10 onDestroy(unsubscribe);11</script>
1<script>2 import { count } from "./store.js";3 const incrementCount = () => count.update(currentValue => currentValue + 1);4</script>5
6<button on:click="{incrementCount}">Increment Count by One/button></button>
1<script>2 import { count } from "./store.js";3 const reset = () => count.set(0);4</script>5
6<button on:click="{reset}">Reset Count</button>
https://svelte.dev/tutorial/auto-subscriptions
Svelte has yet another convinient way to subscribe to the global state change.
With $ prefixed to a variable, Svelte takes care of both un/subscription out of the box.
Instead of this verbose un/subscribe for count,
1<script>2 import { onDestroy } from "svelte";3 import { count } from "./store";4
5 let countValue;6 const unsubscribe = count.subscribe(value => {7 countValue = value;8 });9 // Clean up after your business!10 onDestroy(unsubscribe);11</script>12
13<p>Count value is {countValue}</p>
You can simply prefix count with $ like $count.
1<script>2 import { onDestroy } from "svelte";3 import { count } from "./store";4</script>5
6<p>Count value is {$count}</p>
Make sure to read notes in the linked page.
https://svelte.dev/tutorial/readable-stores
Readable store provides, duh, read-only store, for which one can initialize but can't update.
It looks similar to useEffect that the returned function is called when "the last subscriber unsubscribes".
store.js
1import { readable } from "svelte";2
3const initialValue = new Date();4const valueUpdator = set => {5 const id = setInterval(() => set(new Date()), 1000);6
7 // called when the last subscriber unsubscribes.8 return () => clearInterval(id);9};10
11export const time = readable(initialValue, valueUpdator);
And the same as wriable store, you can refer to it with $ prefix, like $time in another file.
The tutorial prefixes time with $ like $time in the callback.
Auto-subscriptions tutorial states that
Any name beginning with $ is assumed to refer to a store value. It's effectively a reserved character โ Svelte will prevent you from declaring your own variables with a $ prefix.
But I tried it without $ prefix as shown below but still works.
export const elapsed = derived(time, t => Math.round((t - start) / 1000));
Not sure if $ is required. Left a question on Reddit.
https://www.reddit.com/r/sveltejs/comments/hblmxa/question_derived_callback_in_tutorial_uses_a/
https://svelte.dev/tutorial/custom-stores
One can create a custom store by implementing subscribe method.
Tutorial uses wriable's subscribe to expose the interface and doesn't show how to implement one yourself.
https://svelte.dev/tutorial/store-bindings
Store value referred to with $ prefix can be bound as if it's a local state.
1<script>2 import { name } from "./store.js";3</script>4
5<input bind:value="{$name}" />
Typing in the input will update $name and will trigger update itself and all dependents.
https://svelte.dev/tutorial/tweened
Svelte has a built-in motion library without having to install a 3rd party library.
In React, you'd use react-spring, or react-motion, etc.
https://svelte.dev/tutorial/spring
Use this instead of tweened for frequently changing values
https://svelte.dev/tutorial/transition
Another batteries-included way to provide transition in JavaScript.
According to Chrome Devtools, <p transition:fade> injects an inline style to fade in/out.
1<script>2 import { fade } from "svelte/transition";3 let visible = true;4</script>5
6{#if visible}7<p transition:fade>Fade in and out</p>8{/if}
https://svelte.dev/tutorial/adding-parameters-to-transitions
You can also pass in-line parameters to transition functions in the markup.
1<script>2 import { fly } from "svelte/transition";3 let visible = true;4</script>5
6<input type="checkbox" bind:checked="{visible}" />7
8{#if visible}9<p transition:fly="{{ y: 200, duration: 2000 }}">Flies in and out</p>10{/if}
Transitions are "reversible".
Toggling visibility doesn't abruptly starts transition from beinging or the end.
It reverses where it left off.
Refer to the linked tutorial page to see it in action! Cool stuff.
https://svelte.dev/tutorial/in-and-out
You can granularly contorl transition with in & out directives instead of transition.
https://svelte.dev/tutorial/custom-css-transitions
Looks simple so long as you undersand CSS transition and motions etc.
I know neither well so it's tough.
To learn first: Using CSS transitions on MDN.
https://svelte.dev/tutorial/custom-js-transitions
Use tick callback to implement JS transitions for effects not possible by CSS transitions.
https://svelte.dev/tutorial/transition-events
Monitor transition directive events with following directives
https://svelte.dev/tutorial/local-transitions
local transition makes transitions to occur on individual elements, not for a group of items.
Honestly, I really haven't found a use for this.
https://svelte.dev/tutorial/deferred-transitions
More advanced transition concept I'd have to learn later.
https://svelte.dev/tutorial/animate
Oh boy. come back later...
https://svelte.dev/tutorial/actions
Use use: directive to specify the action.
1<script>2 import { pannable } from "./pannable.js";3</script>4<div use:pannable></div>
pannable is a function, which accepts a DOM node.
1// Fires following custom events2// 1. panstart3// 2. panmove4// 3. panend5export function pannable(node) {}
When the pannable dispatches a custom event, the parent component can subscribe to it in the markup.
1<script>2 import { pannable } from "./pannable.js";3
4 // These functions have access to `event` dispatched from `pannable`5 const handlePanStart = event => {};6 const handlePanMove = event => {};7 const handlePanEnd = event => {};8</script>9<div10 use:pannable11 on:panstart="{handlePanStart}"12 on:panmove="{handlePanMove}"13 on:panend="{handlePanEnd}"14 style="transform:15 translate({$coords.x}px,{$coords.y}px)16 rotate({$coords.x * 0.2}deg)"17></div>
Clean up of the action can be done by exposing onDestroy.
1export function pannable(node) {2 return {3 onDesotry() {4 // clean up the mess5 },6 };7}
https://svelte.dev/tutorial/adding-parameters-to-actions
Just like transitions, actions can accept arguments.
1<script>2 import { longpress } from "./longpress.js";3</script>4<div use:longpress="{duration}"></div>
When the duration is changed, longpress.js won't know that the duration has changed.
To subscribe to the duration change, implement update function in the action
longpress.js
1export function longpress(node, duration) {2 return {3 update(newDuration) {4 duration = newDuration;5 },6 };7}
Multiple arguments can be passed to the action as an object
1<script>2 import { longpress } from "./longpress.js";3</script>4<div use:longpress="{{duration," spiciness}}></div>
and accept the object in the action.
longpress.js
export function longpress(node, { duration, spiciness }) {}
https://svelte.dev/tutorial/classes
Svelt provides a shortcut for class toggle.
It's just class in Svelte not className as it is in React.
1<script>2 let current = "foo";3</script>4<style>5 .someActiveClass {6 background-color: red;7 color: white;8 }9</style>10
11<button class:someActiveClass="{current='foo'}" on:click="{() => current = 'foo'}">>foo</button>12
13<button class:someActiveClass="{current='bar'}" on:click="{() => current = 'bar'}">>bar</button>14
15<button class:someActiveClass="{current='baz'}" on:click="{() => current = 'baz'}">>baz</button>
Whenever the condition matches, the custom class append after class: is added.
https://svelte.dev/tutorial/class-shorthand
The shorthand for the shortcut (whew, what a mouthful) is that you can leave out the directive assignment if the class to toggle matches the variable name.
<div class:big="{big}"></div>
can be shortened to
<div class:big></div>
https://svelte.dev/tutorial/slots
This is just like React's children to specify where to put child components in the current one.
Svelte component is not a function, but more like a markup w/ scripts and styles.
So to access children, you need to specify <slot></slot> or <slot />.
You can specify multiple <slot />, which will show the children multiple times.
box.svelte
1<style>2 .box {3 }4</style>5
6<div class="box">7 <slot></slot>8 <!-- or -->9 <slot />10</div>
And pass the children to the box component.
1<script>2 import Box from "./box.svelte";3</script>4
5<Box>6 <h1>Here is the child header</h1>7 <p>this is the content</p>8 <p></p9></Box>
Personal note: This is more to how React should have been as React's supposed to be declarative.
Svelte correctly uses the markup declration for the child, while React is imperative with children. (Not to mention children can be anything like a function to implement render props).
https://svelte.dev/tutorial/slot-fallbacks
If you weren't specifying any fallback, you could use <slot /> but to provide a fallback (when a user didn't specify a child), then you can use a longer <slot>fallback content</slot>.
box.svelte
1<style>2 .box {3 }4</style>5
6<div class="box">7 <slot>Fallback content!!!</slot>8</div>
The example of none-child passed to Box is as shown below
1<script>2 import Box from "./Box.svelte";3</script>4
5<Box>6 <h2>Hello!</h2>7 <p>This is a box. It can contain anything.</p>8</Box>9
10<Box></Box>11<Box />
https://svelte.dev/tutorial/named-slots
In React, one would expose seprate components or static child components like this.
1function App() {2 return (3 <ContactCard>4 <ContactCard.Name>Sung Kim</ContactCard.Name>5 <ContactCard.Address />6 </ContactCard>7 );8}9// or10function App() {11 return (12 <ContactCard>13 <ContactCardName>Sung Kim</ContactCardName>14 <ContactCardAddress />15 </ContactCard>16 );17}
It requires to create seprate component for ContactCardName or ContactCardAddress, each of which accepts its own children function.
This is where things get interesting.
You can specify which "slot" you want to insert the child content into!
ContactCard.svelte
1<style>2 .missing {3 }4</style>5
6<article class="contact-card">7 <h2>8 <slot name="name">9 <span class="missing">Unknown name</span>10 </slot>11 </h2>12
13 <div class="address">14 <slot name="address">15 <span class="missing">Unknown address</span>16 </slot>17 </div>18
19 <div class="email">20 <slot name="email">21 <span class="missing">Unknown email</span>22 </slot>23 </div>24</article>
As shown in the previous section, each named slots contain fallbacks.
The calling component specifies the slot in the child component
App.svelte
1<script>2 import ContactCard from "./ContactCard.svelte";3</script>4
5<ContactCard>6 <span slot="name">Sung</span>7 <span slot="email">Sung@sung.com</span>8</ContactCard>
https://svelte.dev/tutorial/slot-props
Passing data from slot to the parent component, one needs to declare the exposed state (via slot) while declaring the component
You don't declare a variable in the parent component but just sorta like "bind" using let.
Hovering.svelte: a component containg a slot.
1<script>2 let hovering;3
4 const enter = () => (hovering = true);5 const leave = () => (hovering = false);6</script>7
8<div on:mouseenter="{enter}" on:mouseleave="{leave}">9 <slot hovering="{hovering}"></slot>10 <!-- or use the hsort hand -->11 <!-- <slot hovering></slot> -->12</div>
To access hovering in the parent component, use let as mentioend before.
Parent.svelte
1<script>2 import Hoverable from "./Hoverable.svelte";3</script>4
5<Hoverable let:hovering="{hovering}">6 <div class:active="{hovering}">7 {#if hovering}8 <p>I am being hovered upon.</p>9 {:else}10 <p>Hover over me!</p>11 {/if}12 </div>13</Hoverable>
Note that hovering variable is not declared in the script but could be used inside Hovering.
https://svelte.dev/tutorial/context-api
Svelte's Context API is similar to that of React;
Only decendant child components can access context data using getContext expoed via setContext in the parent.
store is more like Zustand where state is avaiable anywhere in the component hierachy.
Difference between React & Svelte Context API is that, React's API is declarative using a markup, Svelte imperative, using setContext during component initialization.
React
1function App() {2 return <Context.Provider value={value}>children can access context value here</Context.Provider>;3}
https://svelte.dev/tutorial/svelte-self
To recursively refer the current component.
There is a typo in the documentation so filed an issue: https://github.com/sveltejs/svelte/issues/5044
Update: "a file" refers to the current file, not the File component. So the documentation is correct. Clsoed the issue.
https://svelte.dev/tutorial/svelte-component
Use <svelte:component this={component}> to load a component dynamically.
To pass props, pass it to <svelte:component>.
<svelte:component text="custom text" this="{selected.component}" />
text is then passed to selected.component (not documented in the tutorial just found out by mistake).
Make sure that the dynamic component accepts the prop.
e.g.) RedThing.svelte
1<style>2 strong {3 color: red;4 }5</style>6
7<script>8 export let text = "red thing";9</script>10
11<strong>{text}</strong>
https://svelte.dev/tutorial/svelte-window
It's a declarative way to add events to window object.
https://svelte.dev/tutorial/svelte-window-bindings
Turns out, you can also bind to some of window's properties, not just events.
https://svelte.dev/tutorial/svelte-body
This lets you bind events declaratively in the document.body.
https://svelte.dev/tutorial/svelte-head
Injecting content inside <html><head>.
No need for react-helmet like 3rd party library.
https://svelte.dev/tutorial/svelte-options
advanced Svelte compiler options.
Most notably, you can specify immutibility to optimize component render in a list.
https://svelte.dev/tutorial/sharing-code
This looks like a "static" variable avaiable throughout the all the instances of a component.
Possibly a prototype value.
https://svelte.dev/tutorial/module-exports
Exporting within module level script can be imported from another Svelte component.
https://svelte.dev/tutorial/debug
The better "console.log" :p