Scott Whittaker

Software Engineer

Day job React | side projects Svelte

Sapper Router Demo

Svelte looks very appealing, I have read through the examples and wanted to look at how routing works before going any further. Sapper is the framework for Svelte that handles routing, I am going to dip my toe in with a demo that is similar to the one I did for Aurelia in a previous post; a simple demo showing how nested routes work.

View the demo.

Install Sapper

At the time of writing Sapper is in early development so things are likely to change quickly, right now I am following the getting started guide which instructs as follows…

npx degit "sveltejs/sapper-template#rollup" sapper-router-demo
# or: npx degit "sveltejs/sapper-template#webpack" sapper-router-demo
cd sapper-router-demo
npm install
npm run dev

Marvellous, we are now up and running at localhost:3000.

Cleanup

Note that for the sake of this demo I have removed the blog page generated by the Sapper project template and simplified the views so there are only titles allowing us to just concentrate on routes.

Nested Routes

See the documentation for nested routes.

In order to test out nested routes lets first add a new page called profile.

  • Within the routes directory add a new directory called profile
  • Within the profile directory add a new file called _layout.svelte

This layout component will be applied to all child pages of profile. In this file lets add a heading and slot.

src/routes/profile/_layout.svelte

<h2>Profile</h2>
<slot></slot>

We now have a new page that we need to show on the navigation bar. In the navigation component we need to add another link to our profile page as follows…

src/components/Nav.svelte

<li><a class="{segment === 'profile' ? 'selected' : ''}" href="profile">profile</a></li>

Now in the UI we have a profile link but if you click it the 404 page is displayed. That is because a layout alone will not get rendered, we need either an index.svelte which will be loaded by default for the above link (href="profile") or we need to be explicit and point to another file/directory within the profile directory, for example href="profile/account", this is a nested route.

Lets add some more nested pages to profile so we end up with the file tree below.

├── routes
│   ├── _error.svelte
│   ├── _layout.svelte
│   ├── about.svelte
│   ├── index.svelte
│   └── profile
│       ├── _layout.svelte
│       ├── account
│       │   ├── index.svelte
│       ├── email
│       │   └── index.svelte
│       └── notifications
│           └── index.svelte
  1. add a new directory called account in src/routes/profile
  2. add a new directory called email in src/routes/profile
  3. add a new directory called notifications in src/routes/profile

Then in each of those directories add an index.svelte file with a heading, for example in account we will have…

src/routes/profile/account/index.svelte

<h2>Account</h2>

Now lets go back to our main navigation component and fix the 404 issue we were seeing. Modify the href attribute to point to our account page as follows.

src/components/Nav.svelte

<li><a class="{segment === 'profile' ? 'selected' : ''}" href="profile/account">profile</a></li>

Now when we click on the profile link we navigate to /profile/account and the index page is loaded by default and we see our Account heading displayed underneath the Profile heading.

But what about our other nested pages, how do we access those? We need to create another menu so we can access account, email and notifications from within the profile page. Lets go back to profile layout and add some navigation.

src/routes/profile/_layout.svelte

<script>
	export let segment;
</script>

<style>
	.selected {
		color: rgb(255, 62, 0);
	}
</style>

<h1>Profile</h1>

<nav>
	<ul>
		<li>
			<a class="{segment === 'account' ? 'selected' : ''}" href="profile/account/">account</a>
		</li>
		<li><a class="{segment === 'email' ? 'selected' : ''}" href="profile/email">email</a></li>
		<li>
			<a class="{segment === 'notifications' ? 'selected' : ''}" href="profile/notifications"
				>notifications</a
			>
		</li>
	</ul>
</nav>

<slot></slot>

Now when we refresh the page we see our profile page with nested routes account, email and notifications. Note that above there is some simple styling for the currently selected menu item. We could of course style this menu further but right now lets leave it as is.

More Nesting

Lets add more nesting under profile/account to show that we can keep nesting as much as we like - add username and password pages.

  1. add a new directory called username in src/routes/profile/account
  2. add a new directory called password in src/routes/profile/account

Then in each of those directories add an index.svelte file with a heading, just as we did earlier.

Again, we need to create a menu for these nested pages so we need a _layout.svelte file in src/routes/profile/account. In this case we already have our index.svelte file in the account directory so we can just rename it to _layout.svelte and add some navigation.

src/routes/profile/account/_layout.svelte

<script>
	export let segment;
</script>

<style>
	.selected {
		color: rgb(255, 62, 0);
	}
</style>

<h2>Account</h2>

<nav>
	<ul>
		<li>
			<a class='{segment === "username" ? "selected" : ""}' href="profile/account/username"
				>Username</a
			>
		</li>
		<li>
			<a class='{segment === "password" ? "selected" : ""}' href="profile/account/password"
				>Password</a
			>
		</li>
	</ul>
</nav>

<slot></slot>

Now when we click profile in the main menu we get a 404 due to renaming the file. To fix this we need to modify our profile link in the main navigation component.

src/components/Nav.svelte

- <li><a class:selected='{segment === "profile"}' href='profile/account'>profile</a></li>
+ <li><a class="{segment === 'profile' ? 'selected' : ''}" href="profile/account/username">profile</a></li>

Now when we click on profile in the main menu we see that we default to the username view - profile > account> username.

There is one last modification to make in src/routes/profile/_layout.svelte to make sure that when we click the account link we navigate to our default account view which is username.

src/routes/profile/_layout.svelte

<nav>
	<ul>
-		<li><a class="{segment === 'account' ? 'selected' : ''}" href="profile/account/">account</a></li>
+		<li><a class="{segment === 'account' ? 'selected' : ''}" href="profile/account/username">account</a></li>
		<li><a class="{segment === 'email' ? 'selected' : ''}" href="profile/email">email</a></li>
		<li><a class="{segment === 'notifications' ? 'selected' : ''}" href="profile/notifications">notifications</a></li>
	</ul>
</nav>

Conclusion

There we have it, by following a few conventions we have nested routes in sapper.