If we are going to be building a web app with sapper we ought to know what sapper is and what it does, right? so what is sapper?
What is Sapper?
Sapper is a framework for building fullstack web apps that is built on top of svelte, think of it like next.js and react. You must already know how to build svelte apps and components for you to understand this article, if you are totally new to svelte, then i advice you to take a look at this article to get an understanding of how svelte works. Sapper works with svelte components and shares a few similarities with next js, Sapper was built by [Jordan Walke]. Sapper offers some advanced features that can make your svelte projects great, some includes;
- Sever Side Rendering (SSR)
- Static Site Generation(SSG)
- One code base for both server side code and front end code
- Search Engine Optimization (SEO)
You can go through the introductory blog to know more about sapper and what it can do for you. Some of the features of sapper we will be looking at in this article includes;
- Installation of sapper
- Sapper app structure
- Routing
- Preloading data
- Redirects, error and layout
- SEO
- Building for production(SSR)
- Exporting a static sapper site (SSG)
Installing Sapper
installing node js
To install sapper, you need to have node js installed on your device first, you can head to nodejs website to download and install the latest version of node js on your device.
installing Degit
Once you have node js on your device the next thing is to install degit. degit is a package that allows you to easily clone the latest commit from a github repo. To install degit open up a terminal and type npm i degit -g
and hit enter. Once that is done we have to clone the sapper template repo to our machine locally.
cloning sapper
To clone the sapper template repo, from your command line/terminal enter npx degit "sveltejs/sapper-template#webpack" app-name
and press enter, app-name here refers to the name of the folder you want the repo to cloned into, you can call it whatever you like, i'll call it sapper-app. Once that is done, navigate into the folder via the command line and then enter npm i
to install the dependencies. Once that is done, we can now serve the app by hitting npm run dev
from the command line and press enter, open up a browser and navigate to localhost:30000 and you should see the app.
Sapper App Structure
By default the folder that structure that come along with the sapper template will look like this;
---------------------package.json
|
|--------node_modules
|
|--------src/--------routes/
| |---client.js
| |---server.js
| |---service-worker.js
| |---template.html
|
|--------static/
|--------webpack.config.js
Let's walk through the folder structure briefly,
the src folder is where we write our code. It contains within it another folder, the routes folder, we will discuss this folder shortly. The client.js file imports and kick starts our sapper app. The service-worker.js file is a proxy server. The template.html file is a template file that is sent to the browser
The static folder is where all our assets go and it is served from root level.
webpack.config.json is a webpack config file.
Routing
Routing in svelte is treated like routing in next js, if you have experience working with next js then you should find it simple and straight foward. Each svelte component inside the routes folder is treated like a route, the default template project comes with a few components. At the root level of the routes folder, there must be an index.svelte component, so when we visit localhost:3000, it is this index.svelte that gets served to the browser and that is our home page.
Delete all the components inside the routes folder except the index.svelte and _layout.svelte component, let's add our own custom components
If we wanted to add a new route all we need to do is create a svelte component inside the routes folder and the name that we used to save the file becomes a route and if we visit that route in the browser the component whose name matches that route will be served to us. Say we create a component called heroes and save it as heroes.svelte inside of our routes folder. When we visit localhost:3000/heroes, sapper will serve that heroes.svelte component to us. let's see a typical use case;
our heroes.svelte component should look like this
// heroes.svelte
<script>
let heroes = ['superman', 'spiderman', 'batman']
</script>
<main>
{#each heroes as hero}
<h2>Contact us</h2>
{/each}
</main>
<style>
h2{
text-align: center
}
</style>
Our routes folder should look like this;
src/routes---------------index.svelte
|
|--------heroes.svelte
|
|--------_layout.svelte
And if we visit localhost:3000/heroes we should see the app running on our machine;
We can also create a folder inside the routes folder name it heroes and inside the contact folder we create an index.svelte file and if we visit localhost:3000/heroes the index.svelte file will still get served up;
src/routes---------------index.svelte
|
|--------/heroes/index.svelte
|
|--------_layout.svelte
Dynamic Routing
The above instances we just discussed are for static routes that don't change, if we wanted to handle dynamic routing we would have to do things a bit different, say we have a list of contact and when we click on each contact we want to see information about that contact, we would have to create another component inside the contact folder, however we dont just give it a name, when we want to name the component we use a special naming convention, we use [name].svelte, much like in next js. The name is the dynamic link that will change and we tell sapper that this route is a dynamic one by ensuring that the name of the component is inside square brackets, let's see a typical use case;
Our routes folder should look like this;
src/routes---------------index.svelte
|
|--------heores/-------index.svelte
|
|---[hero].svelte
|
|--------_layout.svelte
our [hero].svelte should look like this
// [hero].svelte
<script>
</script>
<main>
<h2>Dynamic route</h2>
</main>
<style>
h2{
text-align: center
}
</style>
Now if we visit localhost:3000/heroes/spiderman or localhost:3000/heroes/superman we will see the [contact].svelte component served up. We will discuss how to extract the dynamic route from the component in the next section
Preloading Data
Inside every component thatv serves as a route can export a function called preload, it is best if this called within a script tag that has an attribute of context="module". This preload will run on the server before the HTML is sent to the browser. Inside this function we have access to two parameters, page which represents the current component we are trying to visit, and session which hold some information about the current session. To extract the dynamic part of a url that is [hero] in [hero].svelte we can call the preload method and inside the params argument we can access a params object that holds the dynamic part of the url as an object, let's see a typical example;
// [hero].svelte
<script context="module">
export async function preload (page, params) {
const { hero } = page.params //hero holds the dynamic part of the url
}
</script>
<main>
<h2>Dynamic route</h2>
</main>
<style>
h2{
text-align: center
}
</style>
We can then use this information to fetch a data from an endpoint or API, the preload function provides us the following;
- To fetch the data using a this.fetch() method which is similar to the fetch API/
- To redirect the user using a this.redirect(route) to redirect a user and
- To show up a custom 404 page using this.error(status, message) if something goes wrong.
Let's use this.fetch to get details aboute a hero and pass it as props to a component
// [hero].svelte
<script context="module">
export async function preload (page, session) {
const { hero } = page.params //hero holds the dynamic part of the url
const res = await this.fetch(url)
const myHero = await res.json()
return { myHero }
}
</script>
<script>
export let myHero
</script>
<main>
<h2>Dynamic route</h2>
<h4>{ myHero.name }</h4>
</main>
<style>
h2{
text-align: center
}
</style>
The advantage of this function is that before the browser loads the component the data has been fetched and is available making our application feel smooth
Redirects, error and layout
Redirects
We can redirect a user to another page inside the preload function, we can use this.redirect(status, route). Status is the status of the redirect, and route is the route want to redirect the user, let's see how we can redirect the user to home page from [hero].svelte, open up your [hero].svelte file and make the following changes to it.
// [hero].svelte
<script context="module">
export async function preload (page, session) {
const { hero } = page.params
if (hero.match(/\D/)){
this.redirect(301, '/') //redirecting the user to the home page
}
const res = await this.fetch(url)
const myHero = await res.json()
return { myHero }
}
</script>
<script>
export let myHero
</script>
<main>
<h2>Dynamic route</h2>
<h4>{ myHero.name }</h4>
</main>
<style>
h2{
text-align: center
}
</style>
We use a regExp to test for the presence of an alphanumeric value in the dynamic route and if there is one, we redirect the user to the route /
and this route maps to the index.svelte component in the root routes folder/
Error handling
We can define a custom component to be displayed by sapper whenever the user visits a route that doesn't map to any component we simply name the file _error.svelte
and this way svelte automatically know that this isn't a component that needs a route, whenever we want to declare a component inside the routes folder and we don't want it to be mapped to a route we start the name of the component with an underscore. Inside the error component we have access to the status of the error and the error itself and then we can use that in the template to display information to the user.
Our routes folder should look like this;
src/routes---------------index.svelte
|
|--------heores/-------index.svelte
| |
| |---[hero].svelte
|
|--------_layout.svelte
|
|--------_error.svelte
our _error.svelte should look like this
//_error.svelte
<script>
export let error //error object
export let status
</script>
<main>
<h3> { hero }</h3>
<h5> opps something happened wrong, here is what we found { error.message }</h5>
</main>
<style>
</style>
Now if we visit any route that doesn't exist we should see the _error.svelte component.
#### Layout
We can define a custom layout for any route and all pages that is on the folder level or nested within it will be displayed according to the layout. To define as a layout file it should be named with _layout.svelte. Inside this component we have access to a variable name segment that represents the current page we are on we can then register any other component that we know is going to be a consistent part of our UI.
//_layout.svelte
<script>
export let segment //represents current page
</script>
<main>
<nav> you nav bar goes here</nav>
<slot></slot> <!-- each component that maps to a route will be rendered here -->
<footer>footer goes here</footer>
</main>
<style>
</style>
### SEO
We can add a head tag to our routes components so we can add meta tags, all we do is simply use a component that sapper provides for us, the <svelte:head></svelte:head>
then we can add a title to the document or meta tags.
// [hero].svelte
<script context="module">
export async function preload (page, session) {
const { hero } = page.params
if (hero.match(/\D/)){
this.redirect(301, '/') //redirecting the user to the home page
}
const res = await this.fetch(url)
const myHero = await res.json()
return { myHero }
}
</script>
<script>
export let myHero
</script>
<svelte:head>
<title>{ myHero.name }</title>
<meta name="hero" content={myHero.name} />
</svelte:head>
<main>
<h2>Dynamic route</h2>
<h4>{ myHero.name }</h4>
</main>
Building for production (SSR)
When we want to build a sapper app for production we just run npm run build && npm start
from the terminal, make sure you are inside the directory of the app in the terminal. Our production ready app will be housed inside an __sapper__/build
folder and we can host this on a node js server. That's it dead easy!
Exporting a static site (SSG)
When we want to export a static site we can run sapper export
and sapper will build a static website for us and we can then export it to a static host and that's it.
Whew! That's it for now, there are more stuffs that we can do with sapper than we touched we can do stuffs like server side code, because by default sapper comes baked with a polka server that we can modify; or even replace with express, so the list is endless. However I'll still write about some cool things you can do with sapper.