Exploring Sapper + Svelte: A quick tutorial
Tags: tutorial, javascript
Published: 2019-07-08 20:23:55

---
title: Exploring Sapper + Svelte: A quick tutorial
published: true
tags: tutorial, javascript
canonical_url: https://blog.logrocket.com/exploring-sapper-svelte-a-quick-tutorial/
---

![](https://thepracticaldev.s3.amazonaws.com/i/e4gf5enbcs1xbqoakade.jpg)

## Introduction

A while back, [we explored Svelte.js](http://We'll%20be%20taking%20a%20high-level%20look%20at%20Sapper,%20what%20it%20is,%20how%20it%20helps%20you%20build%20full-fledged,%20lightweight%20apps,%20and%20why%20you%20should%20consider%20using%20it.%20Note:%20This%20post%20is%20a%20followup%20to%20the%20intro%20to%20Svelte%20article%20published%20a%20while%20back.%20I%20strongly%20urge%20you%20to%20read%20it%20here:%20https://blog.logrocket.com/truly-reactive-programming-with-svelte-3-0-321b49b75969/.) and saw how it helps you write truly reactive apps while shipping far less code than many other frontend frameworks out there. While you could very well build a more complex app with Svelte alone, it might get messy real quick. Enter Sapper!

In this article, we’ll be taking a high-level look at Sapper, how it helps you build full-fledged, lightweight apps, and break down a server-rendered app. ![😁](https://s.w.org/images/core/emoji/12.0.0-1/72x72/1f601.png)By the end of this article, you should know enough of Sapper to understand what makes it awesome.

With that said, it’s still a good idea to go through the [documentation](http://sapper.svelte.dev/docs#Introduction(opens%20in%20a%20new%20tab)), as there are some concepts covered there that I left out.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## What is Sapper?

[Sapper](https://sapper.svelte.dev/docs) is the companion component framework to Svelte that helps you build larger and more complex apps in a fast and efficient way.

In this modern age, building a web app is a fairly complex endeavor, what with code splitting, data management, performance optimizations, etc. That’s partly why there is a myriad of frontend tools in existence today, but they each bring their own level of complexity and learning curves.

Building an app shouldn’t be so difficult, right? Could it be simpler than it is right now? Is there a way to tick all the boxes while retaining your sanity? Of course there is — that was a rhetorical question!

Let’s start with the name: Sapper. I’ll just go ahead and [quote the official docs](https://sapper.svelte.dev/docs#Why_the_name) on why the name was chosen:

> In war, the soldiers who build bridges, repair roads, clear minefields and conduct demolitions — all under combat conditions — are known as **sappers**.

> For web developers, the stakes are generally lower than for combat engineers. But we face our own hostile environment: underpowered devices, poor network connections, and the complexity inherent in front-end engineering. Sapper, which is short for **Svelte app maker** , is your courageous and dutiful ally.

Hmm, makes perfect sense. ![🤓](https://s.w.org/images/core/emoji/12.0.0-1/72x72/1f913.png)

Sapper (and, by extension, Svelte) is designed to be lightweight, performant, and easy to reason about while still providing you with enough features to turn your ideas into awesome web apps.

Basically, here are the things Sapper helps take care of for you when building web apps in Svelte:

- **Routing**
- **Server-side rendering**
- **Automatic code splitting**
- **Offline support (using Service Workers)**
- **High-level project structure management**

I’m sure you’d agree that managing those yourself could quickly become a chore distracting you from the actual business logic.

But talk is cheap — code is convincing! Let’s walk through a small server-rendered app using Svelte + Sapper.

## Hands-on experience

Instead of me telling you how Sapper helps you build apps easily, we are going to explore the demo app you get when you scaffold a new project and see how it works behind the scenes.

To get started, run the following commands to bootstrap a new project:

```jsx
$ npx degit "sveltejs/sapper-template#rollup" my-app
$ cd my-app
$ npm install
$ npm run dev
```

Doing that will get you a bare-bones project, but that will be enough for the purpose of this article. We should be able to explore how Sapper handles routing and server-side rendering with this simple project without going too deep.

Let’s dive in!

## Project structure

Sapper is an opinionated framework, meaning certain files and folders are required, and the project directory must be structured in a certain way. Let’s look at what a typical Sapper project looks like and where everything goes.

### Entry points

Every Sapper project has three entry points along with a `src/template.html` file:

1. `src/client.js`
2. `src/server.js`
3. `src/service-worker.js` (this one is optional)

#### `client.js`

```jsx
import * as sapper from '@sapper/app';

sapper.start({
  target: document.querySelector('#sapper')
});
```

This is the entry point of the client-rendered app. It is a fairly simple file, and all you need to do here is import the main Sapper module from `@sapper/app` and call the `start` method from it. This takes in an object as an argument, and the only required key is the `target`.

<!-- /wp:paragraph -->

The target specifies which DOM node the app is going to be mounted on. If you’re coming from a React.js background, think of this as `ReactDOM.render`.

#### `server.js`

We need a server to serve our app to the user, don’t we? Since this is a Node.js environment, there are tons of options to choose from. You could use an Express.js server, a Koa.js server, a Polka server, etc., but there are some rules to follow:

1. The server must serve the contents of the `/static` folder. Sapper doesn’t care how you do it. Just serve that folder!
2. Your server framework must support middlewares (I personally don’t know any that don’t), and it must use `sapper.middleware()` imported from `@sapper/server`.
3. Your server must listen on `process.env.PORT`.

Just three rules — not bad, if you ask me. Take a look at the `src/server.js` file generated for us to see what it looks like in practice.

#### `service-worker.js`

If you need a refresher on what Service Workers are, [this post should do nicely](https://developers.google.com/web/fundamentals/primers/service-workers/). Now, the `service-worker.js` file is not required for you to build a fully functional web app with Sapper; it only gives you access to features like offline support, push notifications, background synchronization, etc.

Since Service Workers are custom to apps, there are no hard-and-fast rules for how to write one. You can choose to leave it out entirely, or you could utilize it to provide a more complete user experience.

#### `template.html`

This is the main entry point for your app, where all your components, style refs, and scripts are injected as required. It’s pretty much set-and-forget except for the rare occasion when you need to add a module by linking to a CDN from your HTML.

#### `routes`

The MVP of every Sapper app. This is where most of your logic and content lives. We’ll take a deeper look in the next section.

## Routing

If you ran all the commands in the **Hands-on experience** section, navigating to `http://localhost:3000` should take you to a simple web app with a homepage, an about page, and a blog page. So far, so simple.

Now let’s try to understand how Sapper is able to reconcile the URL with the corresponding file. In Sapper, there are two types of routes: page routes and server routes.

Let’s break it down further.

### Page routes

When you navigate to a page — say, `/about` — Sapper renders an `about.svelte` file located in the `src/routes` folder. This means that any `.svelte` file inside of that folder is automatically “mapped” to a route of the same name. So, if you have a file called `jumping.svelte` inside the `src/routes` folder, navigating to `/jumping` will result in that file being rendered.

In short, page routes are `.svelte` files under the `src/routes` folder. A very nice side effect of this approach is that your routes are predictable and easy to reason about. You want a new route? Create a new `.svelte` file inside of `src/routes` and you’re golden!

What if you want a nested route that looks like this: `/projects/sapper/awesome`? All you need to do is create a folder for each subroute. So, for the above example, you will have a folder structure like this: `src/routes/projects/sapper`, and then you can place an `awesome.svelte` file inside of the `/sapper` folder.

With this in mind, let’s take a look at our bootstrapped app and navigate to the “about” page. Where do you think the content of this page is being rendered from? Well, let’s take a look at `src/routes`. Sure enough, we find an `about.svelte` file there — simple and predictable!

Note that the `index.svelte` file is a reserved file that is rendered when you navigate to a subroute. For example, in our case, we have a `/blogs` route where we can access other subroutes under it, e.g., `/blogs/why-the-name`.

But notice that navigating to `/blogs` in a browser renders a file when `/blogs` is a folder itself. How do you choose what file to render for such a route?

Either we define a `blog.svelte` file outside the `/blogs` folder, or we would need an `index.svelte` file placed under the `/blogs` folder, but not both at the same time. This `index.svelte` file gets rendered when you visit `/blogs` directly.

What about URLs with dynamic slugs? In our example, it wouldn’t be feasible to manually create every single blog post and store them as `.svelte` files. What we need is a template that is used to render all blog posts regardless of the slug.

Take a look at our project again. Under `src/routes/blogs`, there’s a `[slug].svelte` file. What do you think that is? Yup — it’s the template for rendering all blog posts regardless of the slug. This means that any slug that comes after `/blogs` is automatically handled by this file, and we can do things like fetching the content of the page on page mount and then rendering it to the browser.

Does this mean that any file or folder under `/routes` is automatically mapped to a URL? Yes, but there’s an exception to this rule. If you prefix a file or folder with an underscore, Sapper doesn’t convert it to a URL. This makes it easy for you to have helper files inside the routes folder.

Say we wanted a helpers folder to house all our helper functions. We could have a folder like `/routes/_helpers`, and then any file placed under `/_helpers` would not be treated as a route. Pretty nifty, right?

### Server routes

In the previous section, we saw that it’s possible to have a `[slug].svelte` file that would help us match any URL like this: `/blogs/<any_url>`. But how does it actually get the content of the page to render?

You could get the content from a static file or make an API call to retrieve the data. Either way, you would need to make a request to a route (or endpoint, if you think only in API) to retrieve the data. This is where server routes come in.

From the official docs: “Server routes are modules written in `.js` files that export functions corresponding to HTTP methods.”

This just means that server routes are endpoints you can call to perform specific actions, such as saving data, fetching data, deleting data, etc. It’s basically the backend for your app so you have everything you need in one project (you could split it if you wanted, of course).

Now back to our bootstrapped project. How do you fetch the content of every blog post inside `[slug].svelte`? Well, open the file, and the first bit of code you see looks like this:

```jsx
<script context="module">
  export async function preload({ params, query }) {
    // the `slug` parameter is available because
    // this file is called [slug].html
    const res = await this.fetch(`blog/${params.slug}.json`);
    const data = await res.json();
    if (res.status === 200) {
      return { post: data };
    } else {
      this.error(res.status, data.message);
    }
  }
</script>
```

All we are looking at is a simple JS function that makes a GET request and returns the data from that request. It takes in an object as a parameter, which is then destructured on line 2 to get two variables: `params` and `query`.

What do `params` and `query` contain? Why not add a `console.log()` at the beginning of the function and then open a blog post in the browser? Do that and you get something like this logged to the console:

```jsx
{slug: "why-the-name"}slug: "why-the-name"\_\_proto\_\_: Object {}
```

Hmm. So if we opened the “why-the-name” post on line 5, our GET request would be to `blog/why-the-name.json`, which we then convert to a JSON object on line 6.

On line 7, we check if our request was successful and, if yes, return it on line 8 or else call a [special method](https://sapper.svelte.dev/docs#this_error) called `this.error` with the response status and the error message.

Pretty simple. But where is the actual server route, and what does it look like? Look inside `src/routes/blogs` and you should see a `[slug].json.js` file — that is our server route. And notice how it is named the same way as `[slug].svelte`? This is how Sapper maps a server route to a page route. So if you call `this.fetch` inside a file named `example.svelte`, Sapper will look for an `example.json.js` file to handle the request.

Now let’s decode [slug].json.js, shall we?

```jsx
import posts from './_posts.js';

const lookup = new Map();
posts.forEach(post => {
  lookup.set(post.slug, JSON.stringify(post));
});

export function get(req, res, next) {
  // the `slug` parameter is available because
  // this file is called [slug].json.js
  const { slug } = req.params;
  if (lookup.has(slug)) {
    res.writeHead(200, {
      'Content-Type': 'application/json'
    });
    res.end(lookup.get(slug));
  } else {
    res.writeHead(404, {
      'Content-Type': 'application/json'
    });
    res.end(JSON.stringify({
      message: `Not found`
    }));
  }
}
```

What we’re really interested in here begins from line 8. Lines 3–6 are just preparing the data for the route to work with. Remember how we made a GET request in our page route: `[slug].svelte`? Well, this is the server route that handles that request.

If you’re familiar with Express.js APIs, then this should look familiar to you. That is because this is just a simple controller for an endpoint. All it is doing is taking the slug passed to it from the `Request` object, searching for it in our data store (in this case, `lookup`), and returning it in the `Response` object.

If we were working with a database, line 12 might look something like `Posts.find({ where: { slug } })` (Sequelize, anyone?). You get the idea.

Server routes are files containing endpoints that we can call from our page routes. So let’s do a quick rundown of what we know so far:

- Page routes are `.svelte` files under the `src/routes` folder that render content to the browser.
- Server routes are `.js` files that contain API endpoints and are mapped to specific page routes by name.
- Page routes can call the endpoints defined in server routes to perform specific actions like fetching data.
- Sapper is pretty well-thought-out.

## Server-side rendering

Server-side rendering (SSR) is a big part of what makes Sapper so appealing. If you don’t know what SSR is or why you need it, [this article](https://medium.com/@baphemot/whats-server-side-rendering-and-do-i-need-it-cb42dc059b38) does a wonderful job of explaining it.

By default, Sapper renders all your apps on the server side first before mounting the dynamic elements on the client side. This allows you to get the best of both worlds without having to make any compromises.

There is a caveat to this, though: while Sapper does a near-perfect job of supporting third-party modules, there are some that require access to the `window` object, and as you know, you can’t access `window` from the server side. Simply importing such a module will cause your compile to fail, and the world will become a bit dimmer. ![🥺](https://s.w.org/images/core/emoji/12.0.0-1/72x72/1f97a.png)

Not to fret, though; there is a simple fix for this. Sapper allows you to import modules dynamically (hey, smaller initial bundle sizes) so you don’t have to import the module at the top level. What you do instead will look something like this:

```jsx
<script>
  import { onMount } from 'svelte';

  let MyComponent;

  onMount(async () => {
    const module = await import('my-non-ssr-component');
    MyComponent = module.default;
  });
</script>

<svelte:component this={MyComponent} foo="bar"/>
```

On line 2, we’re importing the `onMount` function. The `onMount` function comes built into Svelte, and it is only called when the component is mounted on the client side (think of it like the equivalent of React’s `componentDidMount`).

This means that when only importing our problematic module inside the `onMount` function, the module is never called on the server, and we don’t have the problem of a missing `window` object. There! Your code compiles successfully and all is well with the world again.

Oh, and there’s another benefit to this approach: since you’re using a dynamic import for this component, you’re practically shipping less code initially to the client side.

## Conclusion

We’ve seen how intuitive and simple it is to work with Sapper. The routing system is very easy to grasp even for absolute beginners, creating an API to power your frontend is fairly straightforward, SSR is very easy to implement, etc.

There are a lot of features I didn’t touch on here, including preloading, error handling, regex routes, etc. The only way to really get the benefit is to actually build something with it.

Now that you understand the basics of Sapper, it’s time for you to go forth and play around with it. Create a small project, break things, fix things, mess around, and just get a feel for how Sapper works. You just might fall in love. ![❤](https://s.w.org/images/core/emoji/12.0.0-1/72x72/2764.png)

* * *

## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps

[![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)](https://logrocket.com/signup/)

[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

[Try it for free](https://logrocket.com/signup/).

* * *

The post [Exploring Sapper + Svelte: A quick tutorial](https://blog.logrocket.com/exploring-sapper-svelte-a-quick-tutorial/) appeared first on [LogRocket Blog](https://blog.logrocket.com).