Layouts

In this module, we will learn how to use the SvelteKit layout component.
Table of Contents
Course Version History
  • Nov. 21, 2022 - Updated to SvelteKit v1.0.0-next.549. Updated from using the old file based router to the new folder based router. Also updated to use grouped layouts in place of named layouts.

In the last module we went over routing to our pages, and up to this point, we've treated our pages as standalone components: upon navigation, the existing page is destroyed, and a new one takes its place. But this is not necessarily the case. Oftentimes, there are elements that should be displayed on every page. For example, you may want a header and footer to always exist throughout the app. Thankfully with SvelteKit, this is very intuitive to do. Instead of adding these to every single page file, we can use layout components.

Pages can be wrapped in a layout component using the <slot> tag, which indicates where the child component should be placed within the parent layout. If you're unfamiliar with slots, you can check out one of my previous modules in this course on the topic.

To create a layout component that applies to every page, in our routes folder, we need to create a file called +layout.svelte, which you may remember making back when we covered CSS. This layout component needs to contain a single <slot> tag, but we can add whatever markup, styles, and behavior we want as well.

Within our layout file we have some html and tailwind classes that are creating our header and shopping cart, as well as some logic to show and hide our cart. This means that every single page in our application will display our header, and will also be able to open and close our shopping cart. If we check out some different routes we can confirm this. Now, we can put anything we want in this file, the only rule is that it must contain a slot tag for our pages to be inserted into. Just remember, whatever you add to this layout will be shown on every page in your app.

We can also create nested layouts similar to how we used nested routes. Each folder within our routes folder can have its own layout. In our example, we created a root layout that will be applied to every page in our app, but we can also create a layout file in our product folder that will only be applied to the pages within the folder. Let's create a +layout.svelte file in our product folder to add a suggested items section to these pages.

/src/routes/product/+layout.svelte
<script>
  import CollectionCard from '$lib/CollectionCard.svelte';
  import { setContext } from 'svelte';
  let newItems = [
    {
      name: 'Graphic T',
      src: 'https://cdn.shopify.com/s/files/1/0434/0285/4564/products/Front-NoModel_ec3be051-d579-4c03-b55b-64449d0b0445.png?v=1623255893',
      price: '$60.00',
    },
    {
      name: 'Jacket',
      src: 'https://cdn.shopify.com/s/files/1/0434/0285/4564/products/bomber-jacket-0.png?v=1622902777',
      price: '$80.00',
    },
  ];
  setContext('newItemsContext', newItems);
</script>
 
<slot />
<div class="pb-40">
  <CollectionCard />
</div>

Now if we check this out in our browser, we only see our root layout, but if we navigate to /product/cup, we see both the root layout as well as our product layout. It is important to remember that adding a nested layout will not cancel out the root layout, it will display both.

By default, the layout hierarchy mirrors the route hierarchy. However, there may be some instances where this isn't necessarily what you want. For instance, within our src/routes/product/apparel directory we have two pages, our root page and /t-shirt page. What if we wanted to add two different layouts to each of these pages?

Let's go ahead and try to do this. First let's add the following layout to our apparel folder:

/src/routes/product/apparel/+layout.svelte
<div class="">
  <div class="p-4 text-center font-bold text-white">
    Hoodies on sale for a limited time only!
  </div>
  <slot />
</div>

And within our /t-shirt directory we will add another layout like this:

/src/routes/product/apparel/t-shirt/+layout.svelte
<div class="">
  <div class="p-4 text-center font-bold text-white">
    Hurry, this product is almost out of stock!
  </div>
  <slot />
</div>

Now, if we navigate to /product/apparel we see our layout that says 'Hoodies on sale for a limited time only!', and if we navigate to /product/apparel/t-shirt we see both of our new layouts. This is because the layout in the /t-shirt directory inherits the parent layout in the /apparel directory. However, this is not what we want. We only want the layout within the /t-shirt directory to apply to this page.

To do this, we can group these routes with a directory whose name is wrapped in parentheses. Let's go ahead and move the root page and layout into a directory called (app) and our t-shirt directory into one called (item) like this:

src/routes/product/apparel
├ (app)/
│ ├ +page.svelte
│ └ +layout.svelte
└ (item)/
  ├ t-shirt/
  │ └ +page.svelte
+layout.svelte

Directories whose names are wrapped in parentheses, such as (app) and (item), do not affect the URL pathname of the routes inside them. Now, any page our layout from one group will not inherit any layout from the other group.

Pages can break out of the current layout hierarchy on a route-by-route basis. To exemplify this, let's add another page, /cup/+page.svelte, within our (item) group. Ordinarily, this would inherit the root layout, the /product layout, and the /(item) layout. We can reset to one of those layouts by appending @ followed by the segment name — or, for the root layout, the empty string. For example, if we only want to inherit the root layout, we can name our page like this: [email protected]. Now in the browser if we route to /product/apparel/cup we do not see the product or (item) layouts, only the root layout is present. If we update our page name to be [email protected], we now see both the root and product layout, but not our (item) layout.

Like pages, layouts can break out of their parent layout hierarchy, using the same technique. For example, a [email protected] component would reset the hierarchy for all its child routes.

The last thing to go over in this module is error pages. If a page fails to load, SvelteKit will render an error page. For example, it we try to route to a page that doesn't exist, we will get this default error page. When a page fails to load, SvelteKit will render the error page within the layout, if one exists. If you do not supply an error page, SvelteKit will use the default one, which we see here.

You can customize this page by creating error components alongside your layout and page components. Just like for layouts, each folder within the routes folder can have an error page. We create these error components by naming a file +error.svelte. Let's create an error page in the root of our routes folder.

+error.svelte
<h1>There was an error loading this page</h1>

This is the root error page, which means it will be displayed for all pages unless a different error page is specified.

That covers everything layout related. We now have an efficient way to share specific components, styles, and logic throughout our app without destroying it on each page change. In the next module, we will learn about SvelteKit endpoints.