Check out a free preview of the full Fullstack Svelte with SvelteKit course:
The "Advanced Routing" Lesson is part of the full, Fullstack Svelte with SvelteKit course featured in this preview video. Here's what you'd learn in this lesson:

Rich provides examples of SvelteKit's advanced routing features, including optional parameters, rest parameters, parameter matchers, routing groups, and breaking out of the layout hierarchy. Routing groups can be a convenient way to share UI and data-loading logic between different routes.

Get Unlimited Access Now

Transcript from the "Advanced Routing" Lesson

[00:00:00]
>> So in the earlier chapter on routing, we learned how to create routes with dynamic parameters. But sometimes it's helpful to make a parameter optional. A really good example of this is when you're using the path name to determine the locale. So you might begin your routes with slash fr for your French users, slash de for your German users, and so on.

[00:00:18] But you also want to have a default locale. So at the moment, we're doubling things up. We have our default page.svelte here, and then we also have the language-specific ones up here. And this one here is kind of duplicative, we wanna get rid of that. So we can change this lang directory here to use double brackets instead, and that makes that parameter optional.

[00:00:39] Just gonna hit the Rename button there and change that to lang with double brackets. And the app is now failing to build because we have a conflict between this route and this route. In other words, it's ambiguous, SvelteKit doesn't know which of these is supposed to apply if you hit the '/' route.

[00:01:01] So we're gonna just delete this altogether. And then finally, we're gonna edit the page.server.js inside the lang directory to specify a default local. So here's our internationalization data here. I'm just gonna add a fallback here for when lang is undefined. Sometimes you'll have a route that has an unknown number of path segments.

[00:01:35] And in these cases, we can use what's called a rest parameter to match any number of segments from zero to whatever. And it's named after its resemblance to the rest parameters in JavaScript. So let's take this src/routes/[path] directory and rename it to dot dot dot path.

[00:01:56] Right, this will now match any route whatsoever. So we can keep clicking this and it will still match the current route Until eventually we reset. And when you have a situation like this, other more specific routes will be tested first. So the rest parameter is essentially a catchall.

[00:02:16] This is useful to have in nested inside other directories. For example, if you want custom 404 pages for different parts of your app, then you can create a catchall that has its own error.svelte and its own data loading. Inside the load function, you could throw in a 404 error and it would render that error page instead of the fallback error page.

[00:02:39] Now, unlike in some frameworks, you do not need to put your rest parameters at the end of the route, you can have them inside. So something like /item/[...path]/edit, is a totally valid route, or /items/[...path].json

[00:02:53] is also totally valid. Sometimes we want to add some matching logic to our routing. For example, you might want a route like colors/value to match hex values like ff3e00 or 676779. But you don't wanna match named colors like colors/octarine or any other arbitrary input like this invalid value here.

[00:03:17] And we can do that with a thing called a matcher. First, we create a new file in a directory called params. I'm gonna call this one hex.js. And inside this module, I'm gonna export a function called match. And the job of this function is to return true if the parameter is a successful match, and false if it's not.

[00:03:49] So the regular expression for a hex code is, let's see, we want the caret to match the start, and then we want to have any number of 0 to 9, a to f. No, not any number, we want six of them, followed by the end of the match string.

[00:04:07] I'm gonna test the value against that. Right, and then to use the new matcher, we're gonna rename this [color] directory to [color=hex]. Right, and so we can continue navigating between the colors, but if we now navigate to the invalid route, it's gonna be a 404. That does not match colors equal hex.

[00:04:41] And so these matches, they run on the server when you're doing routing on the server, and they also run in the client. So you need to make sure that you're not accessing any server-side information inside your matches. As we saw earlier in the workshop, layouts are a way to share user interface and data loading logic between different routes of your application.

[00:04:58] Or sometimes it's useful to have layouts that don't affect the route. For example, you might have a /app and /account routes, and those need to be behind authentication while your /about route page is open to the world. And we can do this with a thing called a route group, which is a directory in parentheses.

[00:05:18] So first thing we're gonna do, is we're gonna rename the account directory here to authed/account with the authed in parentheses. Right, and we can navigate to the account page just as we could before, nothing has changed. Even though we have this directory in the tree, that's not reflected in the routing structure.

[00:05:45] We're gonna move app into the same directory. Again, I'm just gonna hit that Rename button and prefix it with authed in parentheses, right, and these two things are now grouped, and we can navigate to all the same routes that we could before. But what we can do now, now that we have that route group is, we can control access to those routes by creating a layout.server.js inside the authed directory.

[00:06:10] So just click on that, and then create the layout.server.js file. And we're gonna redirect the user if we detect that they're not logged in, and pull the redirect helper from SvelteKit. Then we're gonna export a load function. And if the user doesn't have the logged_in cookie, Then we're gonna throw a redirect and send them to the log in page.

[00:06:55] But we're gonna redirect them once they do log in back to wherever we currently are. Right, so now if we try to visit the account page or the app page, it's gonna send us to the log in page instead. The log in route has a form action which sets the logged_in cookie when we press the Login button.

[00:07:27] I'm gonna hit that. And now I go to the account page. We can add some UI to these two routes that's distinct from the UIs on the rest of the app by adding a layout.svelte component inside the authed directory. So hit that button there and create layout.svelte. Then inside here, we're gonna create a form, method = POST, action = logout.

[00:08:11] And create that Logout button, followed by the slot so that we can put the page content in. So now if I'm on the account page or the app page, I can logout, and I'll get kicked back out to the homepage where I don't need to be authenticated. So that's how you create a layout group without affecting the route.

[00:08:34] Sometimes we need to do the opposite thing, sometimes we need to break out of layouts. Ordinarily, every page inherits every layer above it so that if we have a page like abc/page.svelte, then it will inherit all of these layouts. The route layout, the a layout, the b layout, and the c layout.

[00:08:55] And you can see that in this exercise if we navigate between these different pages, each of these layouts is getting applied in turn. And sometimes it's useful to be able to break out of the current layout hierarchy. And we can do that by adding the '@' sign, followed by the name of the parents segment that we want to reset to.

[00:09:13] So for example, if we were to change this file from +page.svelte to +page@b.svelte, then it will put the abc route inside the b layout instead of the c layout. So now these two routes share a layer, or we could change it to a. And so now we go from home to a, to the b layout, back to the a layout.

[00:09:43] Or we could reset it all the way to the top by specifying the empty string. And so now we go home, a, b, and then back to the route layout. Right, the route layout here, this applies to every page of your app, you cannot break out of the route layout.

[00:10:01] So if you have some parts of your app that shouldn't have any layout UI whatsoever, then what you'll need to do is put every other part of your app inside a route group. Something like parentheses app, and then put all of your routes inside that, and then you can use +page@ to reset all the way to the blank layout at the very top.

[00:10:21]
>> In this SvelteKit docs regarding the data SvelteKit preload code, hover and tap have this same description, what's their difference?
>> So the difference between hover and tap is that if a link has the data SvelteKit preload data attribute set to hover, then when the mouse comes to rest over that link, SvelteKit will begin fetching the code and the data for the next page.

[00:10:52] The assumption being that if the mouse has come to arrest over the link, there's a very high chance that the user is about to initiate a navigation. But if you don't want that, then you can specify tap. And in that case, the preloading won't begin until there's a pointer down then.

[00:11:10] That doesn't give you quite as much of a head start, but it does reduce the number of false positives that you'll encounter. So if you're trying to save data, then that's a good thing for you to do. But if you're trying to maximize the speed of navigation, then you'll want to use hover.

[00:11:24] Obviously, hover only applies to desktop because on a mobile device, you don't have a mouse, there are no hover events. So in that case, it will fall back to the tap behavior in all cases. If you do have hover set, then if for whatever reason the the hover hasn't yet been triggered because the mouse clicks on the link while it's still moving over the link, then it will also initiate navigation on tap.

[00:11:54]
>> Is the +page@.svelte the same as +layout@.svelte?
>> Yeah, so you can apply a layout reset to your layout components as well as to your page components if you want a whole group of routes to be reset to an earlier layout separately, altogether, I guess. Yeah, I hope that makes sense.

[00:12:19]
>> Is there a way to have named layouts?
>> The way that you name a layout is just by having it inside a segment. So wherever you are in the tree, you can refer to a layout above the route that you're currently at just by referring to the name of the directory in which that layout is.

[00:12:38]
>> To be clear, a layout that you can refer to that isn't route-based?
>> No, because if you had something like that, then the layouts would no longer be bound to the hierarchy and the behavior would be very difficult to reason about. So the name of the layout is always connected to the directory in which that layout lives.

[00:13:01] If we started having two ways of naming layouts, then it would just add confusion, but you wouldn't actually gain any expressive power, so we don't do that.