reCAPTCHA WAF Session Token

A Beginner’s Guide to SvelteKit — SitePoint


SvelteKit is an officially supported framework, built around Svelte. It adds key features to a Svelte app — such as routing, layouts and server-side rendering — and makes frontend development outrageously simple.

In this tutorial, we’ll take a beginner-friendly look at both Svelte and SvelteKit and build out a simple web app showing profile pages of imaginary users. Along the way, we’ll look at all the main features that SvelteKit has to offer.

Let’s start by looking at what Svelte brings to the table.

Table of Contents
  1. The Benefits of Working with Svelte
  2. So, Why Do I Need SvelteKit?
  3. Prerequisites
  4. Getting Started
  5. Layouts and Client-side Routing
  6. Static Pages and Prerendering
  7. Endpoints
  8. Fetching Data with the load Function
  9. Dynamic Parameters
  10. Prefetching
  11. Conclusion

The Benefits of Working with Svelte

Svelte is growing in popularity, and that’s for a good reason. Developing apps with Svelte is based on writing reusable and self-contained components — similar to other popular JavaScript frameworks such as React.

The big difference comes with its build-time compilation — as opposed to a run-time interpretation of the code. In other words, Svelte already compiles our code during the build process and the final bundle only contains JavaScript that our application actually needs. This results in fast web apps with small bundle sizes.

Other frameworks only parse and bundle up the code we’ve written, essentially taking the component tree as is and shipping it to the client. In order for the browser to be able to interpret it and update the UI, a lot more code needs to be delivered and additional work is done on the client side. (You can read here how React handles this process under the hood.)

Other than that, Svelte is an ideal framework for beginners. Everyone who knows how to write HTML and how to include and tags with basic JavaScript and CSS can already start writing Svelte components.

So, Why Do I Need SvelteKit?

While Svelte alone gives us a very good development experience, we still have to decide how we want to ship our application to the user. The classical approach would be to take our favorite module bundler like webpack or Rollup and bundle our code into one big, fat JavaScript file. Then, we’d call it from a very basic HTML document, like so:

<code class="markup language-markup"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>UTF-8<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
    ...
  <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span>

  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span>
    
    <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>app<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
    
    <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dist/bundle.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span>
</code>

While this is absolutely legit, the user’s experience might not be ideal. There are many touchpoints for improvement and this is where SvelteKit comes into play.

First of all, instead of serving an almost empty HTML file to the client, SvelteKit already comes with all the HTML elements we need for the first page view. The benefits are faster page loads and SEO boosts. There are two ways SvelteKit does this: prerendering and server-side rendering. I’ll explain both in more detail below. What stays the same is that, once the JavaScript has been loaded, it takes over and enables typical features of a single page application, like client-side routing. It’s worth noting that we can also tell SvelteKit to omit the first render on the server and behave like a classical single page application. The framework is very versatile.

The second obvious difference between SvelteKit and a classical single JavaScript bundle is code-splitting. Instead of serving the entire app in one single JavaScript file, SvelteKit splits the code into separate, smaller chunks. Each chunk represents a route of our application. For example, everything that needs to be fetched for the /home and for the /about routes will be loaded once the user actually needs it — or a little bit earlier if we make use of SvelteKit’s prefetching functionality (like we’ll do below).

Another outstanding benefit of SvelteKit is that we can decide which deployment environment our app is going to run in. Nowadays, frontend developers have a variety of different platforms where applications can run. There are hosting providers for simple static files, more advanced serverless options such as Vercel, or server environments where Node servers can be executed, and so on. With tiny plugins called adapters, we tell SvelteKit to optimize our output for a specific platform. This greatly facilitates app deployment.

However, the biggest advantage SvelteKit has to offer is its ease of use. Of course, we can manually set up our build process from scratch with all these features, but this can be tedious and frustrating. SvelteKit makes it as easy as possible for us, and the best way to experience this is by actually using it.

This is why we’ll create a simple web app showing profile pages of made-up users. And along the way, we’ll look at all the features I’ve mentioned above in more detail.

Prerequisites

No previous knowledge is required, although some experience with Svelte might be helpful. The article Meet Svelte 3, a Powerful, Even Radical JavaScript Framework provides a good introduction.

To work with SvelteKit, we’ll need a working version of Node on our system. We can install it using the Node Version Manager (nvm). (You can find some setup instructions here.)

You can find all the code for this tutorial on GitHub.

Getting Started

To begin with, we initiate a new SvelteKit project. Execute the following commands in the terminal:

<code class="bash language-bash"><span class="token function">npm</span> init svelte@latest svelteKit-example-app
</code>

We’ll be asked a few questions so that we can customize our project. For our purposes, answer the following:

  • Which Svelte app template? -> SvelteKit demo app
  • Use TypeScript components -> no
  • Anything else? -> no

This will load a SvelteKit development environment, including a functional example application.

In our project route there are now some configuration files: our package.json, the static folder, and the src folder. We’ll be working mainly inside the src folder. It has the following structure:

<code class="txt language-txt">src
├── app.html
├── lib
│   ├── images
│   │   └── (various images ..)
└── routes
    ├── +layout.svelte
    ├── +page.js
    ├── +page.svelte
    ├── Counter.svelte
    ├── Header.svelte
    ├── styles.css
    ├── about
    │   ├── +page.js
    │   └── +page.svelte
    └── sverdle
        ├── +page.server.js
        ├── +page.svelte
        ├── game.js
        ├── reduced-motion.js
        ├── words.server.js
        └── how-to-play
            ├── +page.js
            └── +page.svelte
</code>

The /src/app.html file is our app-shell — a minimal HTML page where our rendered HTML will be inserted and our bundle files linked from. Usually we don’t have to touch this file. We can insert some app-wide meta tags if we want to, but this isn’t necessary — as we’ll see in a moment.

The /src/routes folder is the heart of our application. Any files inside that have a + prefix are special to SvelteKit. To create a new page, we create a Svelte component named +page.svelte. The folders leading up to this file make up the URL path. For example, /src/routes/test/+page.svelte would be served under the URL /test.

Svelte components can have child components. For example, the route component /src/routes/test/+page.svelte might import a component named Button.svelte. Because all files without a + prefix have no meaning to SvelteKit, we can place these components right next to their routes, resulting in nice colocation. If we have components or utilities that are reused in a lot of places, we should put them in the /src/lib folder.

Let’s see how all this works in action. Change into the newly created directory, then install the dependencies and start the app in development mode:

<code class="bash language-bash"><span class="token builtin class-name">cd</span> svelteKit-example-app
<span class="token function">npm</span> <span class="token function">install</span>
<span class="token function">npm</span> run dev -- --open
</code>

This will open the preexisting example app in a new browser tab. Click through the app and assure yourself it’s working.

Some preparation

As polished as the demo app is, it contains a bunch of files that we won’t need. Let’s get rid of those.

Delete the contents of the lib folder:

<code class="bash language-bash"><span class="token function">rm</span> src/lib/*
</code>

Delete the routes/sverdle folder:

<code class="bash language-bash"><span class="token function">rm</span> -rf src/routes/sverdle
</code>

Delete the counter and header component:

<code class="bash language-bash"><span class="token function">rm</span> -rf src/routes/Counter.svelte
<span class="token function">rm</span> -rf src/routes/Header.svelte
</code>

We can do without the demo app’s styling. In the root of the routes folder, open styles.css and replace the contents with the following:

<code class="css language-css"><span class="token selector"><span class="token pseudo-class">:root</span></span> <span class="token punctuation">{</span>
  <span class="token property">font-family</span><span class="token punctuation">:</span> -apple-system<span class="token punctuation">,</span> BlinkMacSystemFont<span class="token punctuation">,</span> <span class="token string">'Segoe UI'</span><span class="token punctuation">,</span> Roboto<span class="token punctuation">,</span> Oxygen<span class="token punctuation">,</span> Ubuntu<span class="token punctuation">,</span> Cantarell<span class="token punctuation">,</span>
    <span class="token string">'Open Sans'</span><span class="token punctuation">,</span> <span class="token string">'Helvetica Neue'</span><span class="token punctuation">,</span> sans-serif<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">body</span> <span class="token punctuation">{</span>
  <span class="token property">margin</span><span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code>

Finally, open src/routes/+page.svelte and replace the contents with the following:

<code class="markup language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>HOME<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span>
</code>

With that done, let’s get to building out our demo.

Layouts and Client-side Routing

As mentioned above, every +page.svelte component in the routes folder defines one route. But what about code that should apply to many pages at once? For this, we have the layout component, named +layout.svelte. This component contains code that applies to every page next to it and below it.

Let’s open the existing /src/routes/+layout.svelte file. All it does for now is import some app-wide CSS code, provide navigation and a element that wraps the rest of the application. Let’s replace the content with the following:

<code class="markup language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript">
  <span class="token keyword module">import</span> <span class="token string">'./styles.css'</span><span class="token punctuation">;</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token namespace">svelte:</span>head</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>robots<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>noindex<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token namespace">svelte:</span>head</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>.<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>HOME<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/about<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>ABOUT<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>slot</span> <span class="token punctuation">/></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css">
  <span class="token selector">nav</span> <span class="token punctuation">{</span>
    <span class="token property">padding</span><span class="token punctuation">:</span> <span class="token number">1</span><span class="token unit">rem</span><span class="token punctuation">;</span>
    <span class="token property">box- <a href="https://yourselfhood.com/from-pixels-to-the-cloud-the-advantages-of-storing-and-accessing-images-online/"  class="lar_link" data-linkid="4967" data-postid="4007"  title="tag"   target="_blank" >shadow</a></span><span class="token punctuation">:</span> <span class="token number">-1</span><span class="token unit">px</span> <span class="token number">1</span><span class="token unit">px</span> <span class="token number">11</span><span class="token unit">px</span> <span class="token number">4</span><span class="token unit">px</span> <span class="token hexcode color">#898989</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token selector">a</span> <span class="token punctuation">{</span>
    <span class="token property">text-decoration</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
    <span class="token property">color</span><span class="token punctuation">:</span> <span class="token color">gray</span><span class="token punctuation">;</span>
    <span class="token property">margin-right</span><span class="token punctuation">:</span> <span class="token number">1</span><span class="token unit">rem</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span>
</code>

Note: if you want to have syntax highlighting for Svelte files, there are extensions you can install. This one is good for VS Code.

In this example, we used the element to define meta tags that will be inserted in the of our document. Since we did this in the layout component at the root, it will be applied to the entire app. The robots tag is just an example.

Furthermore, we created a navbar. This is a typical use case for the layout component, as it’s usually intended to be shown on every page of our application.

The navbar has two links: one to the root of the application — which already has content served by the /src/routes/+page.svelte component — and one to the about page. The about page was also created by the demo app. Open it and replace its content with the following:

<code class="markup language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>ABOUT<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>hr</span> <span class="token punctuation">/></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>A website to find user profiles<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css">
  <span class="token selector">main</span> <span class="token punctuation">{</span>
    <span class="token property">font-size</span><span class="token punctuation">:</span> <span class="token number">1.5</span><span class="token unit">rem</span><span class="token punctuation">;</span>
    <span class="token property">margin</span><span class="token punctuation">:</span> <span class="token number">4</span><span class="token unit">rem</span><span class="token punctuation">;</span>
    <span class="token property">padding</span><span class="token punctuation">:</span> <span class="token number">2</span><span class="token unit">rem</span><span class="token punctuation">;</span>
    <span class="token property">color</span><span class="token punctuation">:</span> <span class="token color">gray</span><span class="token punctuation">;</span>
    <span class="token property">justify-content</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
    <span class="token property">box- <a href="https://yourselfhood.com/from-pixels-to-the-cloud-the-advantages-of-storing-and-accessing-images-online/"  class="lar_link" data-linkid="4967" data-postid="4007"  title="tag"   target="_blank" >shadow</a></span><span class="token punctuation">:</span> <span class="token number">4</span><span class="token unit">px</span> <span class="token number">5</span><span class="token unit">px</span> <span class="token number">11</span><span class="token unit">px</span> <span class="token number">10</span><span class="token unit">px</span> <span class="token color">lightgray</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span>
</code>

This page is pretty basic. We included some HTML and applied some styling.

Let’s go back to the browser and navigate to the new page. Our modifications should already be visible and we should see something like what’s pictured below.

Let’s navigate between the landing page and the about page. We’ll see that changing the page doesn’t refresh the entire application. The navigation feels smooth and instant. This is because SvelteKit applies Client-Side Routing out of the box. Although we used normal tags in our navbar, SvelteKit identifies those as internal links and intercepts them using its built-in client router.

Static Pages and Prerendering

As noted above, SvelteKit uses the concept of adapters to build apps for different environments. Adapters are imported in the svelte.config.js file.

When we open this configuration file, we can see that our application currently uses the auto adapter. This will optimize the build output for certain deployment targets such as Vercel or Netlify and, by default, every page of our application will be rendered upon request by a Node server. However, this seems a little bit too much, considering the current state of our app. Also, we might not want to run a server for our application.

As our app doesn’t currently depend on any dynamic data, it could consist entirely of static files. And there’s an adapter-static that we can install, which turns SvelteKit into a static site generator. It would render our entire app into a collection of static files during the build process. However, this would prevent us from creating additional pages that depend on server-side rendering.

As we don’t want to turn all our pages into static files, we’ll make use of another SvelteKit feature which enables us to prerender individual files of our application. In our case, we’d like the about page to be prerendered, since it consists of static content and rendering the page on every request would be unnecessary. We can achieve this by adding the following code snippet to our /src/routes/about/+page.svelte page:

<code class="javascript language-javascript"><span class="token keyword module">export</span> <span class="token keyword">const</span> prerender <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
</code>

We can test this out by switching the adapter to adapter-node. For this, we replace @sveltejs/adapter-auto with @sveltejs/adapter-node both in our package.json (also change the version to ^1.0.0) and our svelte.config.js. After installing it with npm install, run npm run build. This will generate a functioning Node server inside the /build folder. As you can see, there’s an HTML file /build/prerendered/about.html containing the prerendered HTML for the about page.

We can run the generated Node server with node build/index.js.

Endpoints

Now it’s time to fill our page with some dynamic content. We’ll adjust the landing page such that it shows a list of user avatars. To do so, we need to fetch a list of user information from an API endpoint. Most developing teams have a separate backend. That would be the place to go. However, SvelteKit makes it easy to turn our application full stack using endpoints by creating +server.js files. Since we have no backend, we’ll create such an endpoint.

Instead of using a real database, we’ll generate some mock user data. To do so, we’ll use the faker library. Let’s install it with npm install -D faker.

Now, create a file /src/routes/api/+server.js in a new /api folder. Since the file is called +server.js, it will be treated as an endpoint. The endpoint will become available under /api. Insert the following code:

<code class="javascript language-javascript"><span class="token keyword module">import</span> <span class="token imports">faker</span> <span class="token keyword module">from</span> <span class="token string">'faker'</span><span class="token punctuation">;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> json <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">'@sveltejs/kit'</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function">generateCovers</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span>
  <span class="token punctuation">[</span><span class="token spread operator">...</span><span class="token known-class-name class-name">Array</span><span class="token punctuation">(</span><span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token method function property-access">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> lastName <span class="token operator">=</span> faker<span class="token punctuation">.</span><span class="token property-access">name</span><span class="token punctuation">.</span><span class="token method function property-access">lastName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span> avatar<span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">https://avatars.dicebear.com/api/human/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>lastName<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">.svg</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> lastName <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword module">export</span> <span class="token keyword">function</span> <span class="token constant">GET</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> <span class="token function">json</span><span class="token punctuation">(</span><span class="token function">generateCovers</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code>

This file exports a GET function. As you might already have guessed, it corresponds to the HTTP method GET. All it does is return a JSON object that holds an array of user data created with generateUsers.

The function generateUsers returns an array of 50 objects with properties lastName and avatar. lastName is generated using faker. avatar stores a URL that points to the free DiceBear Avatar API. It generates random avatars using a seed value, which in our case is lastName.

If we had a real database, we could replace generateUsers with something like findUsers and access the database inside this function.

That’s all it needs. Go back to the browser (make sure the app is still running in dev mode npm run dev) and navigate to http://localhost:5173/api. This will load the raw data. Note that creating an endpoint like we did is only necessary if we don’t have a separate backend API to fetch data.

Fetching Data with the load Function

Next, we’ll use the new endpoint to display user data on our landing page. Open the existing /src/routes/+page.svelte page and replace its content with the following:

<code class="markup language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript">
  <span class="token keyword module">export</span> <span class="token keyword">let</span> data<span class="token punctuation">;</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span>
  {#each data.users as { avatar, lastName }}
    <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>{`/${lastName}`}</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>box<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>{avatar}</span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>{lastName}</span> <span class="token punctuation">/></span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>{lastName}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
  {/each}
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css">
  <span class="token selector">main</span> <span class="token punctuation">{</span>
    <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
    <span class="token property">flex-wrap</span><span class="token punctuation">:</span> wrap<span class="token punctuation">;</span>
    <span class="token property">justify-content</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token selector"><span class="token class">.box</span></span> <span class="token punctuation">{</span>
    <span class="token property">padding</span><span class="token punctuation">:</span> <span class="token number">0.25</span><span class="token unit">rem</span><span class="token punctuation">;</span>
    <span class="token property">margin</span><span class="token punctuation">:</span> <span class="token number">1.5</span><span class="token unit">rem</span><span class="token punctuation">;</span>
    <span class="token property">color</span><span class="token punctuation">:</span> <span class="token color">salmon</span><span class="token punctuation">;</span>
    <span class="token property">box- <a href="https://yourselfhood.com/from-pixels-to-the-cloud-the-advantages-of-storing-and-accessing-images-online/"  class="lar_link" data-linkid="4967" data-postid="4007"  title="tag"   target="_blank" >shadow</a></span><span class="token punctuation">:</span> <span class="token number">4</span><span class="token unit">px</span> <span class="token number">5</span><span class="token unit">px</span> <span class="token number">11</span><span class="token unit">px</span> <span class="token number">2</span><span class="token unit">px</span> <span class="token color">lightgray</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token selector"><span class="token class">.box</span><span class="token pseudo-class">:hover</span></span> <span class="token punctuation">{</span>
    <span class="token property">box- <a href="https://yourselfhood.com/from-pixels-to-the-cloud-the-advantages-of-storing-and-accessing-images-online/"  class="lar_link" data-linkid="4967" data-postid="4007"  title="tag"   target="_blank" >shadow</a></span><span class="token punctuation">:</span> <span class="token number">4</span><span class="token unit">px</span> <span class="token number">5</span><span class="token unit">px</span> <span class="token number">11</span><span class="token unit">px</span> <span class="token number">10</span><span class="token unit">px</span> <span class="token color">lightgray</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token selector">img</span> <span class="token punctuation">{</span>
    <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">15</span><span class="token unit">rem</span><span class="token punctuation">;</span>
    <span class="token property">object-fit</span><span class="token punctuation">:</span> contain<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span>
</code>

The data property that the page receives is filled from the load function inside the sibling +page.js, which we’ll create next. Copy the following code into it:

<code class="javascript language-javascript"><span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> error <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">'@sveltejs/kit'</span><span class="token punctuation">;</span>

<span class="token keyword module">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">load</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> fetch <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> res <span class="token operator">=</span> <span class="token keyword control-flow">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">'/api'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword control-flow">if</span> <span class="token punctuation">(</span>res<span class="token punctuation">.</span><span class="token property-access">ok</span><span class="token punctuation">)</span> <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span> users<span class="token operator">:</span> <span class="token keyword control-flow">await</span> res<span class="token punctuation">.</span><span class="token method function property-access">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token keyword control-flow">throw</span> <span class="token function">error</span><span class="token punctuation">(</span><span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code>

The key challenge to fetching data for dynamic content on a page is that there are two ways a user can navigate to it. The first way is from external sources or after a page refresh. This would cause the application to be loaded from scratch and the page to be served by the server. The second way is from internal navigation, in which case the page would be served by the JavaScript bundle on the client side. In the former, the data is fetched by the server, while in the latter, it’s fetched by the client.

SvelteKit offers a very elegant solution for this — the load function. The load function inside a +page.js can run both on the client and on the server, and in both cases will be executed before the component renders.

load receives an object with a fetch property that we can use to fetch data. It behaves identically to the native fetch API. In this example, we use our new endpoint /api to fetch the array of user objects. To pass this data to our component, we return an object with the users property, which stores our user array.

If we had a separate backend API, instead of fetching data from our /api endpoint, we would fetch it within the load function from the backend.

In case load runs on the server, the client will realize that the data has already been fetched and will not make an additional request.

We’ve returned an object from the load function; now we need to retrieve it inside +page.svelte somehow. SvelteKit hands this object to the data prop, so we can access it with export let data inside a tag. This is what we do to access our users.

Next, we visualize all our 50 users using the #each syntax that we know from Svelte. Inside the each block, we have access to a user’s avatar and lastName properties. We use avatar as the value for the src attribute of an tag.

Now our landing page should look like the image below.

Landing Page

So far, we’ve created an endpoint to simulate a database and used load in +page.js to retrieve data from it. The advantage is that we now have an API to access directly through /api, and we can also use the data from it within our same app to visualize it on our landing page. What if we don’t need a standalone /api endpoint, though? What if that data from the server is only meant to be used on that landing page?

In this case, SvelteKit can simplify things greatly for us by providing the data for a page through a load function, inside a +page.server.js file instead of a +page.js file. The additional .server in the file means that this load function always runs on the server. This means we can access our database or similar directly inside it. SvelteKit will wire everything up so that we don’t need to change anything on the consumer side in +page.svelte. On initial server-side rendering, it will execute the load function before returning the HTML, and on client navigation it will do a fetch request under the hood. Let’s use this approach for our next page!

Dynamic Parameters

Each user box on our landing page is an internal link with a /[lastName] route. This is where dynamic parameters come into play. Under the /[lastName] route, we’ll display additional information for the respective user.

Create a new /src/routes/[lastName]/+page.server.js file with the following content:

<code class="javascript language-javascript"><span class="token keyword module">import</span> <span class="token imports">faker</span> <span class="token keyword module">from</span> <span class="token string">'faker'</span><span class="token punctuation">;</span>

<span class="token keyword module">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">load</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> params <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> lastName <span class="token punctuation">}</span> <span class="token operator">=</span> params<span class="token punctuation">;</span>

  <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span>
    user<span class="token operator">:</span> <span class="token punctuation">{</span>
      lastName<span class="token punctuation">,</span>
      firstName<span class="token operator">:</span> faker<span class="token punctuation">.</span><span class="token property-access">name</span><span class="token punctuation">.</span><span class="token method function property-access">firstName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      avatar<span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">https://avatars.dicebear.com/api/human/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>lastName<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">.svg</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span>
      title<span class="token operator">:</span> faker<span class="token punctuation">.</span><span class="token property-access">name</span><span class="token punctuation">.</span><span class="token method function property-access">title</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      phone<span class="token operator">:</span> faker<span class="token punctuation">.</span><span class="token property-access">phone</span><span class="token punctuation">.</span><span class="token method function property-access">phoneNumber</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      email<span class="token operator">:</span> faker<span class="token punctuation">.</span><span class="token property-access">internet</span><span class="token punctuation">.</span><span class="token method function property-access">email</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code>

Notice the dynamic parameter [lastName] in the folder name. We can access this parameter from the params property of the load function. We use it to return the correct values for lastName and avatar in the response. Since we’re inside a +page.server.js file that always runs on the server, we generate some additional mock data for this user with faker directly inside the load function; no need for an additional API endpoint!

Next, we create the UI for that page — /src/routes/[lastName]/+page.svelte — with the following content:

<code class="markup language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript">
  <span class="token keyword module">export</span> <span class="token keyword">let</span> data<span class="token punctuation">;</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>{data.user.firstName} {data.user.lastName}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>box<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{data.user.avatar}<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{data.user.astName}<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Title: {data.user.title}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Phone: {data.user.phone}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Email: {data.user.email}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css">
  <span class="token selector">main</span> <span class="token punctuation">{</span>
    <span class="token property">margin</span><span class="token punctuation">:</span> <span class="token number">4</span><span class="token unit">rem</span><span class="token punctuation">;</span>
    <span class="token property">padding</span><span class="token punctuation">:</span> <span class="token number">2</span><span class="token unit">rem</span><span class="token punctuation">;</span>
    <span class="token property">color</span><span class="token punctuation">:</span> <span class="token color">gray</span><span class="token punctuation">;</span>
    <span class="token property">justify-content</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
    <span class="token property">box- <a href="https://yourselfhood.com/from-pixels-to-the-cloud-the-advantages-of-storing-and-accessing-images-online/"  class="lar_link" data-linkid="4967" data-postid="4007"  title="tag"   target="_blank" >shadow</a></span><span class="token punctuation">:</span> <span class="token number">4</span><span class="token unit">px</span> <span class="token number">5</span><span class="token unit">px</span> <span class="token number">11</span><span class="token unit">px</span> <span class="token number">10</span><span class="token unit">px</span> <span class="token color">lightgray</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token selector">h1</span> <span class="token punctuation">{</span>
    <span class="token property">color</span><span class="token punctuation">:</span> <span class="token color">salmon</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token selector"><span class="token class">.box</span></span> <span class="token punctuation">{</span>
    <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
    <span class="token property">font-size</span><span class="token punctuation">:</span> <span class="token number">1.5</span><span class="token unit">rem</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token selector">img</span> <span class="token punctuation">{</span>
    <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">15</span><span class="token unit">rem</span><span class="token punctuation">;</span>
    <span class="token property">object-fit</span><span class="token punctuation">:</span> contain<span class="token punctuation">;</span>
    <span class="token property">margin-right</span><span class="token punctuation">:</span> <span class="token number">2</span><span class="token unit">rem</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token selector">li</span> <span class="token punctuation">{</span>
    <span class="token property">margin-bottom</span><span class="token punctuation">:</span> <span class="token number">1</span><span class="token unit">rem</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span>
</code>

Like on the home page, we access the return value of the load function with export let data and visualize the data with some basic Svelte syntax.

Now we should be able to navigate back to the landing page and click on any user box. This will open the corresponding user page. We should see something like what’s pictured below.

User Page

Prefetching

There’s one last feature that I’d like to show, and I’m really excited about it. SvelteKit offers the ability to prefetch data for individual pages.

Let’s go back to our /src/routes/+page.svelte page and add the data-sveltekit-preload-data="hover" attribute to the tag, like so:

<code class="markup language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">data-sveltekit-preload-data</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>hover<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>{`/${lastName}`}</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>box<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
</code>

This tells SvelteKit to execute the load function of the corresponding page upon hovering the element.

Try it out by opening the network tab in your browser (see below). Every time you hover over one of the user boxes, a request to /api/[lastName] is made and the data for the corresponding user page is fetched. This saves additional milliseconds and ensures a better user experience.

SvelteKit Prefetching

By the way, this is also a great way to see how SvelteKit applies code splitting out of the box. Reload the page and clear the Network log. Note that the very first time you hover over an avatar, one JavaScript and one CSS file is being loaded. This is the code chunk corresponding to our /src/routes/[lastName]/+page.svelte page. It gets loaded only once per page session. If you hover over another avatar, only the corresponding data gets loaded, but not again the JavaScript and CSS.

We don’t have to necessarily apply the prefetching attribute to the tag. We could also place this attribute on a parent element or even the body element in app.html to prefetch all routes in the app like this. In fact, the Svelte demo app already did it this way! If we prefer, we can also do the prefetching programmatically using the preloadData function of SvelteKit’s $app/navigation module.

Conclusion

Working with SvelteKit feels very intuitive. All in all, it took me only about an hour to learn all the main features and the results are absolutely astonishing. We get blazing-fast, SEO-optimized web apps that provide the best user experience that modern build tools can possibly deliver.

By default, SvelteKit renders our page on the server. On the client it gets progressively enhanced by a highly optimized JavaScript bundle to enable client-side routing. With a few lines of code we can prerender individual pages or prefetch data to enable instant page load and navigation. Features like code splitting ensure that Svelte’s advantage of small compilation output doesn’t get mitigated by large, app-wide bundles.

Last but not least, SvelteKit gives us complete freedom with respect to all its features. There’s always a way to exclude a feature if we prefer to. We could, for example, opt out of server-side rendering entirely and create a classic single page application.

SvelteKit together with Svelte itself is a real game changer to me. And I believe it could be so for many others.

The author has donated his fee for this article to the Svelte Open Collective.





Source link

Leave a Reply

Your email address will not be published. Required fields are marked *

WP Twitter Auto Publish Powered By : XYZScripts.com