An Introduction to SvelteKit

Autor:

Dominik Röschke

In a previous blog post, we took a look at Svelte. This post will introduce you to SvelteKit.

SvelteKit is to Svelte what a mixture of Next.js and Create-React-App (CRA) is to React. It is like CRA in the sense that it is the default way to build Svelte Apps, maintained by the same team as Svelte itself. It’s like Next.js in most other ways, as it brings a lot more than just CRA. There is integrated Serverside Rendering, Filebased Routing and many other things.

While there are other meta-frameworks for Svelte like Crown for eCommerce or elder.js for static sites, this blog post will focus on SvelteKit as it is the recommended default.

SvelteKit has been in development since November 2020, although it’s predecessor, Sapper, was in development before that. On Svelte Summit 2022, at the beginning of September, the first 1.0.0 release candidate got announced.

SvelteKit Development History

The Svelte Maintainers decided to abandon the development of Sapper for a couple of reasons:

  • When development on Sapper started, a module bundler was the only way to do webapps, with webpack or Rollup being the only options.
  • Serverless Computing wasn’t a thing when Sapper started development and it would have been very difficult to get that paradigm shift integrated into Sapper.

SvelteKit development started with the inclusion of Snowpack. During the development processes, Vite got released in it’s current, framework-agnostic version and included a lot of the features the Svelte Maintainers had to code themselves to allow Snowpack to do the things they wanted it to.
Therefore, Vite was chosen as the way forward. An additional bonus was that Vite is compatible with Rollup-plugins, making migrating older Sapper-apps that already included Rollup-plugins easier.

Integrated Serverside Rendering

SvelteKit comes with integrated serverside rendering, no configuration or effort required. But what does serverside rendering even mean?

Serverside rendering means rendering the page on the server side before sending it to the client. This means that the client will receive fully formed HTML that can be displayed immediately.

This immediately brings one of the benefits of serverside rendering to light: The website or app will perform better, since the client device can immediately start displaying the HTML to the user instead of having to execute JavaScript that then creates HTML that can be displayed to the user.

Another advantage lies in Search Engine Optimization. Pre-rendered HTML can easily be indexed by Google Bot and other search engine crawlers. To be able to index a page rendered by JavaScript, a headless browser needs to be started. Google recommends having pre-formed html for faster indexing and better user experience.
Different blogs and articles claim that HTML sites are indexed in a matter of days while pages that rely on JavaScript take weeks to months. Since Google doesn’t publish official statistics for this, there are is no exact data, but the trend that HTML sites are indexed earlier is definitely clear.

The last big benefit of serverside rendering is reliability. With JavaScript in user space many things can happen and without Sentry or similar systems it will never be noticed. The user could be using Internet Explorer or an older Chrome/Firefox/Safari version that doesn’t support some of the JavaScript features the page is using. Some users even deactivate JavaScript execution for safety or privacy reasons and would be unable to interact with your page if it is not pre-rendered on the server!

Speed

Having just a serverside rendered application as described above is great for initial page load speeds, but future user interactions will suffer if each interaction result has to be fetched from the server again. To counter this, SvelteKit hydrates your application once the initial page load is complete.

That means that it will initialize a client-side router that will make the application behave like a single page application once it is initialized. All missing JavaScript will be loaded from the server and all Svelte components will be hydrated with that JavaScript.
Future user interactions with the page can then be handled on the client side and only API calls will have to be sent to the server, making the application feel quicker and more responsive than a traditional server side app.

File-based routing

SvelteKit uses a routing mechanism that is inspired by the file-based routing system popularized by Next.js, but it isn’t quite the same. While Next.Js treats every file in the /pages directory that exports a react-component as a page, with the name of the file defining the path of the page, Svelte only allows one +page.svelte-file per directory. The path is decided by the folders containing the +page.svelte-file.

An picture of filebased routing in SvelteKit

In SvelteKit, all pages of your app will be contained in the /routes-folder. Every file that is directly in the /routes-folder like on the screenshot, applies to your index page /. A route in SvelteKit can consist of the following files:

  • +layout.svelte
  • +layout.js
  • +page.svelte
  • +page.js
  • +server.js

As you can see in the Screenshot, you can also use Typescript and the .ts ending instead of .js.

In the +layout.svelte you can define the layout of the page in the current and all sub-directories. This is useful, if you have an always visible page-header or footer, or a navigation sidebar. In the +layout.svelte there needs to be a <slot></slot> element, this is where the elements of your +page.svelte will be rendered.

Layouts can be nested, so if you, for example, have a settings-section you can embed your settings navigation above all settings-pages with a single +layout.svelte in the settings directory.

The +layout.js file is used to load data that is required for all child-pages. By exporting a function called load(), data can be fetched and provided to all child pages. The load-Function receives a LoadEvent as a parameter. Especially the fetch attribute of this event is important, since it is a custom fetch implementation.

Data that has been fetched on the server side during SSR will be included in the HTML sent to the client so that it only has to be fetched once!

The +page.svelte file should contain the HTML and Svelte Components that should be rendered for this specific page.

It will be displayed in the <slot></slot> placeholder of it’s containing +layout.svelte-File. If no layout is provided, SvelteKit will display just the page.

The +page.js file works just like the +layout.js file. Data returned by the load-function defined inside of it will only be available to the +page.svelte component instead of all pages in subdirectories though.

If the load-functions should only be called on the server, +layout.js and +page.js can be renamed to +layout.server.js and +page.server.js

The +server.js file can be provided in a directory on it’s own or as part of a page that also displays html. It should export functions that correspond to HTTP verbs like GET, PATCH, PUT, POST, DELETE. In this way a fully functional rest API can be implemented.

If there is both a +page.svelte and a +server.js-file present in the same directory, PUT, PATCH and DELETE requests will be handled by the +server.js automatically. For GET and POST requests, the accept Header makes the difference. If the request prefers text/html, the +page.svelte will be used, otherwise the +server.js takes precedence.

If your page should have a rest parameter in it’s path, for example to be able to link to specific blog entries, the containing folder has to have it’s name in angular brackets like this: [parameter].

This will make the parameter available in the load-function of your +page.js or +layout.js file.

An overview of filebased routing in SvelteKit

To be able to use the parameter in the +page.svelte component, it will have to be returned with the other data your load function delivers to the page. paramName will always correspond to the name of the directory with angular brackets, so to be able to access a parameter with the name paramName, a folder called [paramName] has to be in the directory tree somewhere.

Path parameters can of course be nested, so a path like /category/<categoryname>/entry/<entryname> is possible.

Serverless/Edge first

As mentioned in the history of SvelteKit development, serverless computing and edge networks are at the core of SvelteKit. Every Kit-App has an included adapter that defines how it should be compiled for the platform it should be deployed to. By default, adapter-auto is included in a SvelteKit app. It automatically detects on which environment it is being build and acts accordingly with it’s bundled support packages:

If platform specific features are required, it is easily possible to swap adapter-auto with the platform specific implementation. Apart from these adapters, there is also adapter-static if the application is intended to be a static site or adapter-node if the application should run as a standalone node server or container.

Additionally, there are many community adapters, including adapters to Firebase, Digital Ocean or to build a WordPress plugin which can be found on the Svelte Societies Components page.

Serverless and Edge computing brings a couple of advantages to SvelteKit that are hard to achieve with traditional web applications. Content Delivery Networks (CDN) like Cloudflare allow the deployment of static content at the Edge of the internet, leading to quicker response times to user requests. Serverless Functions can similarly be deployed in an Edge deployment, for example with Vercel’s Edge Functions.

With Serverless Edge Computing it’s possible to achieve quicker responses for users and less headache for developers, since server handling is done by the provider. Netlify, Vercel and Cloudflare also have generous free plans for hobby projects to be able to try out this functionality.

Environment Variables

Easily integrate custom environment variables, either during build or run time with SvelteKits $env utility.

Import private API keys in server-only files like +page.server.js or +server.js to access protected APIs without exposing your credentials to the client.
It is impossible to import /private objects into code that could be available on the client side.

With $env/dynamic/private, runtime environment variables are accessed, allowing, for example, different API keys depending on if the application is deployed in the dev-environment or the production environment.

With $env/static/private environment variables during build time can be accessed. This could be used to configure a dynamic version number or build the same code base with different themes for different sites.

With $env/static/public or $env/dynamic/public, environment information can be shared with the client side as well. To avoid accidental mishaps and exposure of secret values, variables used with /public have to be prefixed with the prefix defined in the SvelteKit config under config.kit.env.publicPrefix, by default it is set to PUBLIC_.

Want to give it a try?

The Svelte Maintainers are currently working on a new set of tutorials for both Svelte and SvelteKit that you can find on learn.svelte.dev.

There is also the next blog post where I show you an app that I developed with SvelteKit.