### Related [[+ Javascript]] --- # Creating a Svelte 5 Project See: * [[New CLI for Svelte 5 Offers More Integrations]] Or, simply remember: ```bash npx sv create my-sv5-app ``` This replaces the old and typical `npm` Svelte creation CLI commands # The Sparks Gist In Svelte 4, the `let` declaration at the top level of a component was implicitly reactive. A `let` declaration anywhere else was _not_ reactive. This means, for one example, you can keep using the same API outside the top level. **In Svelte 5, things are more explicit:** - A variable is *reactive* when created using the `$state(...)` **rune** - Props are now passed with a destructured object, for example: - `let { optional = 'unset', required } = $props();` Event handlers have been simplified. Previously the `:` operator was used to attach a listener to an element, but it has been removed—meaning they are just **props** now. Since they're now properties, you can use the normal short hand syntax, allowing for the following transformation: (_before_) ```svelte <script> let count = $state(0); </script> <button on:click={() => count++}> clicks: {count} </button> ``` (_after_) ```svelte <script> let count = $state(0); function onclick() { count++; } </script> <button {onclick}> clicks: {count} </button> ``` Above, you are now defining a function and then just passing it down. # Reactivity **[[Runes are used to declare reactive state in Svelte 5]]**. **Runes** are basically just a form of signals, which I became briefly familiar with while using Godot, an open source game engine that uses signals to broadcast state to the rest of the game (changes in health, damage output, etc.) * Use `$state()` **[[Svelte 5 uses the $derived rune to derive state from other state]]**. Often, you will need reactive elements that are require data from/are dependent on the states of other reactive elements. * Use `$dervied()` **[[In Svelte 5 derived runes are read only but can still be inspected for tracking]]**. Sometimes you want to keep track of a changing state, but, remember, derived states are read-only because of the underlying use of proxies to make this functionality happen. * Use console logs with `$state.snapshot(derivedState);` for a non-reactive snapshot of state * Alternatively, use `$inspect` to create a reactive snapshot such as `$inspect(numbers); * You can customize how information is console logged by using functions. For example, you can use `console.trace` to see where the state originated from: `$inspect(numbers).with(console.trace); [[In Svelte 5 effects are best thought of as an escape hatch]]. Only use if you have to, and, if you have to, use an event handler to deal with `$effect` # Props [[Data can be injected into other modules in Svelte 5 via the $props rune]]. **Props** is short for properties, and represents the elements that are passed to other components. Default values can also be specified upon declaration of the props in lines like in this file, `Answer.svelte`: ```svelte let { answer } = 'a mystery' } = $props(); ``` Which is imported into `app.svelte` and used like so: ```svelte <script> import Answer from './Answer.svelte'; </script> <Answer answer={42} /> <Answer /> ``` `app.svelte` will output `answer` as 42 and then `a mystery`—the default value declared for the prop You can **spread props** into other components. For example, if you have file `PackageInfo.svelte` which declares props as such: ```svelte let { name, version, description, website } = $props(); ``` And then import it into `app.svelte` and use a "...rest"/destructuring Javascript syntax in place of a bunch of declarations * You can use `let { name, ...stuff } = $props(); * OR, just `let stuff = $props();` and `stuff` will be injected with the array of props passed from `PackageInfo.svelte` # Logic [[Svelte allows you to add logic into your HTML]], despite HTML having no way of expressing it. Svelte allows things like **loops and conditional statements to be possible**. `{#...}` opens a block, `{:...}` extends a block, and `{/...}` closes a block. For example ```svelte {#if count > 10 } <p>{count} is greater than 10</p> {:else if count < 5} <p>{count} is between 0 and 5</p> {:else} <p>{count} is between 5 and 10</p> {/if} ``` To expand functionality and make working with lists of data much easier, we can use an `{#each}` block to save us from writing a ton of declarations. For example, this program creates a reactive array list of colors, and then makes a button out of each of the array values using said `{#each}` block ```svelte <script> const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violent']; let selected = $state(colors[0]); </script> <div> {#each colors as color, i} <button style="background: {color}" aria-label="{color}" aria-current={selected === color} on:click={() => selected = color} >{i + 1}</button> {/each} (/div) ``` This program creates a button for ever color, and tells you the index value of each one based on where it is in the arrow as the text on the button generated because of the `{#each}` block --- [[Unique keys can be made for each blocks to provide specificity when dealing with array values in Svelte]]. In the example program seen at that link, a `const` array called `emojis` (**not reactive**) has things (food) values assigned to emojis of the things, all in a file called `Thing.svelte`. A reactive prop called `{ name }` is created and set equal to those props (so it's equal to the whole array) and is passed into `app.svelte` There, we have a `Remove first thing` button that uses the `array.shift()` Javascript function to remove the first fruit item that is printed using an `{#each}` block. The best way to properly manipulate this list is to assign an id to each of the names (creating `things.id`), and pass that to the `.shift()` function like so: ```svelte <script> import Thing from './Thing.svelte'; let things = $state([ { id: 1, name: 'apple' }, { id: 2, name: 'banana' }, { id: 3, name: 'carrot' }, { id: 4, name: 'doughnut' }, { id: 5, name: 'egg' } ]); </script> <button onClick={() => things.shift()}> Remove first thing </bvutton> {#each things as thing (thing.id)} <Thing name={thing.name} /> {/each} ``` So, pressing that `Remove first thing` button calls the `.shift()` to remove the item. The list of them is generated by iterating through the `id` numbers associated with each name. Remember that `{ name }` is the only `$props()` declared, and that `emoji` is a constant that is set to the value of the `name` reactive prop that is passed into it as a parameter: * `const emoji = emojis[name];` --- In Javascript, a `promise` is an object awaiting the eventual completion or failure of an asynchronous operation. Svelte allows you an easy way to **await the value of a `promise` that goes directly in your markup**. For example: ```svelte {#await promise} <p>...rolling</p> {:then number} <p>You rolled a {number}</p> {:catch error} <p style="color: read">{error.message}</p> {/await} ``` >NOTE: if you know that your promise can't reject, you can omit the `catch` block If you don;'t wait to show anything until the promise resolves (and you know the promise cannot reject), the code becomes this simple bit: ```svelte {#await promise then number} <p>You rolled a {number}</p> {/await} ``` This way, the `You rolled a {number}` is displayed only when the promise is resolved # Events > [!warning] > This section is under construction!