SvelteKit
-
Basic Svelte
- Introduction
- A UI framework that allows you to build your app out of components
- A component is a reusable self-contained block of code that encapsulates HTML, CSS, and JavaScript written into a `.svelte` file
- Components compile into JS modules
- Write CSS in <style> tags, JS in <script> tags inside components
- Style rules are scoped to the component
- Component names are capitalized
- Render HTML directly into a component with `{@html …}` tag
- Reactivity
- Svelte has a reactivity system to keep the DOM in sync with the application state
- Declare derived state with reactive declarations — reactive values - let
count = 0;
$: doubled = count * 2;
- Introduction
-
Valuable when needs to reference them multiple times
-
Reactive declarations and statements will run after other script code and before component markup is rendered
-
Can also run arbitrary statements reactively — log statements to if blocks
-
Svelte’s reactivity is triggered by assignments — array methods won’t trigger it
- Simple rule of thumb: the name of the updated variable must appear on the left-hand side of the assignment
-
Props
- Pass data through the component tree with props
- Declare props with `export` keyword
- Specify default values for props
- export let answer = ‘a mystery’;
-
Logic
-
Conditional render markup with `if` block
- {#if …}…{:else if}…{:else}…{/if}
- # is a block opening tag
- / is a block closing tag
- : is a block continuation tag
-
Render list with `each` block
- {#each listObj as listItem, index (listItem.id)}…{/each}
- listItem.id is key to tell Svelte how to figure out which DOM node to change when the component updates
-
Await values of promises inside `await` block
- {#await promise}
<p>…waiting</p>
{:then number}<p>The number is {number}</p>
{:catch error}
<p style=“color: red”>{error.message}</p>
{/await}
- {#await promise}
-
{#await promise then number}
<p>The number is {number}</p>
{/await}
-
If a second promise is made before the first is resolved, Svelte only considers the most recent promise → no race condition
-
Events
- Listen to DOM events on an element with the `on:` directive
- DOM event handlers can have modifiers to alter their behaviour
- preventDefault --- calls event.preventDefault() before running the handler. Useful for client-side form handling, for example.
- stopPropagation --- calls event.stopPropagation(), preventing the event reaching the next element
- passive --- improves scrolling performance on touch/wheel events (Svelte will add it automatically where it’s safe to do so)
- nonpassive --- explicitly set passive: false
- capture --- fires the handler during the capture phase instead of the bubbling phase
- once --- remove the handler after the first time it runs
- self --- only trigger handler if event.target is the element itself
- trusted --- only trigger handler if event.isTrusted is true, meaning the event was triggered by a user action rather than because some JavaScript called element.dispatchEvent(…)
- Chain modifiers together, e.g. `on:click|once|capture={…}`
- Create custom event handlers for components with event dispatcher
- createEventDispatcher must be called when the component is first instantiated
- Component events don’t bubble up like DOM events, listen to custom events on
deeply nested components by having intermediate components forward the event
- An `on:message` event directive without a value means ‘forward all message events’
- This also works with DOM events
-
Bindings
- Use `bind:` directive for two-way bindings, e.g: form elements
- Also performs type checks for you
- Bind group of radio or checkbox inputs with `bind:group`
- Use `bind:` directive for two-way bindings, e.g: form elements
-
Lifecycle
- Svelte provides functions to run code at key moments during a component’s
lifecycle
- onMount --- runs after the component is first rendered to the DOM
- beforeUpdate --- schedules work to happen immediately before the DOM is updated
- afterUpdate --- runs code once the DOM is in sync with your data
- tick --- returns a promise that resolves as soon as any pending state changes have been applied to the DOM
- Svelte provides functions to run code at key moments during a component’s
lifecycle
-
Stores
- Values that need to be accessed by multiple unrelated components, or by a regular JS module
- A store is an object with a `subscribe` method that allows interested parties to be notified whenever the store value changes
- Writable stores also have `set` and `update` methods
- Not unsubscribing can cause memory leak
- Svelte handles unsubscribing with auto-subscription — reference a store
value by prefixing the store name with `$`
- Only works with store variables that are declared at the top-level scope of a component
- Readable stores prevent writing by users
- Create derived stores with `derived`
- As long as an object correctly implements the `subscribe` method, it’s a store → very easy to implement custom stores to centralize store logic
- If a store is writable, it can bind to its value
-
Advanced Svelte
- Motion
- `svelte/motion` and `svelte/easing` modules
- Change stores or reactive values to `tweened` values
- Use `spring` instead of `tweened` for frequently changing values
- Transitions
- ‘svelte/transition’ modules
- Use `transition` directive
- `fade`, `fly`, etc.
- `in` and `out` directives for in and out transitions
- Can write custom CSS and JS transitions
- Svelte dispatches events for transitions for you to listen to
- `introstart`
- `introend`
- `outrostart`
- `outroend`
- Key blocks destroy and recreate their contents when the value of an
expression changes
- {#key i}…{/key}
- Special elements
- <svelte:self> allows a component to contain itself recursively
- <svelte:window> refers to the window object
- <svelte:body> refers to document.body
- Useful for mouseenter and mouseleave events, which don’t fire on window
- <svelte:document> refers to document
- Useful for selectionchange event, which doesn’t fire on window
- <svelte:head> refers to the <head>
- Useful for <title> and <meta> tags, critical for good SEO
- <svelte:fragment> element allows you to place content in a named slot without wrapping it in a container DOM element.
- Motion
-
SvelteKit
- SvelteKit apps are server-rendered by default and transition to client-side
navigation when the user navigates
- Has 3 main jobs
- Routing — figuring out which route matches an incoming request
- Loading — get the data needed by the route
- Rendering — generate some HTML (on the server) or update the DOM (in the browser)
- Has 3 main jobs
- Routing
- Filesystem-based routing
- Every +page.svelte file inside src/routes creates a page in your app
- +layout.svelte component applies to all routes in the same directory
- The <slot /> element is where the page content will be rendered
- Applies to every child route
- Can nest layouts to arbitrary depth
- Create routes with dynamic parameters with square brackets around a valid
variable name
- Multiple route parameters can appear within one URL segment, as long as they are separated by at least one static character: foo/[bar]x[baz]
- Loading data
- Page data
- Every page of your app can declare a load function in a +page.server.js alongside the +page.svelte file
- This module only runs on the server, including for client-side navigation
- Load data from a database or a CMS
- Access loaded data inside +page.svelte via the data prop
- Access dynamic route parameters by declaring params argument in load function
- Users that visit an invalid pathname can respond with a 404 page with the error helper
- Route data
- +layout.server.js files load data for every child route
- Page data
- Headers & cookies
- Set headers inside a load function, form actions, hooks, and API routes with setHeaders()
- Use the cookies API for cookies-related stuff
- Shared modules
- Because SvelteKit uses directory-based routing, it’s easy to place modules and components alongside the routes that use them
- For modules that are used in multiple places, put them in src/lib directory, and access them from anywhere in src via the $lib alias
- Forms
- Define server-side actions to handle POST request inside +page.server.js
- The request is a standard WebAPI Request object
- Define named form actions with action=”?/<actionName>” in the form and add <actionName> : <action> in actions object in +page.server.js
- The action attribute can be any URL — even if the action was defined on another page
- Validate form data with the browser’s built-in form validation →
server-side validation
- SvelteKit hides unexpected error messages from server-side validation (throw new Error(…)) from users because they contain sensitive data
- Better to provide an indiction of what went wrong with the fail function → return data from the action along with an appropriate HTTP status code (return fail(400, {…})
- Access the returned value via the form prop → only populated after a form submission
- You can also return data from a form action without wrapping it in fail which will also be available via the form prop → useful for showing successful submissions
- Add use:enhance directive to <form> elements to progressively enhance
users’ experience and emulate the browser-native behaviour except for
full-page reloads
- Update the form prop
- Invalidate all data on a successful response, causing load functions to re-run
- Navigate to the new page on a redirect response
- Render the nearest error page if an error occurs
- Provide a callback to use:enhance to add pending states, optimistic UI, cancel() submissions, handle redirects, control whether the form is reset, and so on
- API routes
- Create API routes by exporting functions corresponding to HTTP methods: GET, PUT, POST, PATCH, DELETE in a +server.js file
- Request handlers must return a Response object
- Handlers that mutate data, such as POST should be implemented with form actions for less verbose and more resilient code
- Stores
- SvelteKit makes three readonly stores available via the $app/stores module — page, navigating, and updated
- page provides information about the current page
- url — the URL of the current page
- params — the current page’s parameters
- route — an object with an id property representing the current route
- error — the error object of the current page
- data — the data for the current page, combining the return values of all load functions
- form — the data returned from a form action
- Errors
- There are two types of errors in SvelteKit — expected errors and unexpected errors
- Expected errors
- Create with the error helper from @sveltejs/kit
- Expected errors’ messages are shown to the users
- Unexpected errors
- Create with standard WebAPI Error constructor
- Unexpected errors’ messages are redacted and replaced with a generic ‘Internal Error’ message and a 500 status code
- When the load function fails to run, SvelteKit renders an error page
- Customize the error page with +error.svelte file
- When the root layout data fails to load, or while rendering the error
page, SvelteKit will fall back to a static error page
- Customize the fallback error page with src/error.html file
- Redirect with throw redirect(…) with redirect helper
- Use inside load functions, form actions, API routes, and the handle hook
- Common status codes
- 303 --- for form actions, following a successful submission
- 307 --- for temporary redirects
- 308 --- for permanent redirects
- SvelteKit apps are server-rendered by default and transition to client-side
navigation when the user navigates
-
Advanced SvelteKit
- Hooks
- SvelteKit provides several hooks — ways to intercept and override the framework’s default behavior
- handle — the most elementary hook
- Lives in src/hooks.server.js
- Receives an event object along with a resolve function, and returns a Response
- resolve is where SvelteKit matches the incoming request URL to a route of your app, imports the relevant code, loads the data needed by the route, and generates the response
- Hooks