Pages, Layouts, and Styles

Pages, Navigation, and Layouts


Learning Objectives

  • You know how to create multiple pages in Svelte and know how to navigate between pages.
  • You know how to define a layout and know how to define page-specific content for the head element.

So far in the course, we have built single-page applications, where the content of the page is updated dynamically based the logic in the components and the user interactions. As applications grow, it is often useful to split the application into multiple pages, where each page corresponds to a different view of the application. In this section, we look into creating multiple pages in Svelte, how to navigate between the pages, and how to add a common layout to the pages.

Although we use the term “Svelte”, we are actually using SvelteKit, which is a framework built on top of Svelte. When creating a new project with the command deno run -A npm:sv@latest create client, you are actually creating a SvelteKit project.

Creating pages

When creating our earlier single-page applications, we’ve always modified the file at src/routes/+page.svelte to import one of the components used on the page, which then has often used other components. The file at src/routes/+page.svelte corresponds to the page shown at the root of the application, e.g. at the address http://localhost:5173/.

To create multiple pages, we create new folders in the “src/routes/” folder, placing a “+page.svelte” file in each created folder.

In terms of routing, the name of the folder corresponds to the path of the page. For example, to create a page at the address http://localhost:5173/about, we create a folder called “about” in the “src/routes/” folder, and place a “+page.svelte” file in the folder.

To demonstrate this, create two new folders to src/routes/. Name the first folder about, and the second folder contact. Create a new +page.svelte file to the about folder, placing the following content to the +page.svelte in the folder.

<p>This is my about page!</p>

Then, create a new +page.svelte file to the contact folder, placing the following content to the +page.svelte in the folder.

<p>You can contact me here.</p>

At this point, the contents of the src/routes folder (and its subfolders) should be as follows.

tree --dirsfirst
.
├── about
│   └── +page.svelte
├── contact
│   └── +page.svelte
└── +page.svelte

2 directories, 3 files

Now, when you run the application and open it up in a browser, the page at http://localhost:5173/about looks similar to Figure 1.

Fig 1. The application shows the about page at the path /about.

Fig 1. The application shows the about page at the path /about.

Similarly, the address http://localhost:5173/contact has the contact page with the text “You can contact me here.”.

Loading Exercise...

Navigation between pages is done with hyperlinks, which are created with the a tag in HTML. The href attribute of the a tag specifies the address of the target page. The address can be a URI or a path — in the former case, the link will lead to the URI, while in the latter case the link will lead to a relative location on the current server.

For navigation, we often also use a nav element that represents a section of the page that links to other pages or to parts within the page. As an example, if we would wish to be able to navigate to the paths /, /about, and /contact, we could have a nav element with links as follows.

<nav>
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/about">About</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
</nav>

To demonstrate this, first modify the +page.svelte file in the about folder to match the following.

<p>This is my about page!</p>

<nav>
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/about">About</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
</nav>

And then, modify the +page.svelte file in the contact folder to match the following.

<p>You can contact me here.</p>

<nav>
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/about">About</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
</nav>

Now, when you open up the address http://localhost:5173/about in a browser, you see the about page with links to the home page and the contact page. Similarly, when you open up the address http://localhost:5173/contact, you see the contact page with links to the home page and the about page. Clicking e.g. the link “About” takes you to the about page, while clicking the link “Contact” takes you to the contact page.

Loading Exercise...

The main page at the address http://localhost:5173/ does not contain the links, however. To add the links to the main page, modify the +page.svelte file in the root of the src/routes/ folder to match the following.

<p>Hello world!</p>

<nav>
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/about">About</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
</nav>

Now, when you open up the address http://localhost:5173 in a browser, you see the main page with links to the about and contact pages.

Rule of three

Above, we just hit the rule of three. The rule of three states that when you copy-paste the same code three times, the code should be extracted to a separate method. We can apply the same rule to web development.

As the navigation is the same on all pages, we should extract it to a separate component. However, as it is something that would be the same across pages, it should go to a layout file.

Creating a layout

The term layout refers to the common layout of the application that is used for all (or for the majority) of the pages. Layouts can include content such as a header, a footer, and the functionality for navigating between pages.

In Svelte, the application layout is defined in a file called +layout.svelte that is placed to the folder src/routes/. The layout file is a Svelte component that can contain other Svelte components. It also comes with a special functionality that is used to render the “children” of the layout — that is, the content of each page in the application.

Create the file +layout.svelte to the folder src/routes/, and place the following contents to the file.

<script>
  let { children } = $props();
</script>

<nav>
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/about">About</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
</nav>

{@render children()}

Above, we define a layout that contains a navigation element with links to the home, about, and contact pages. Now, when we open up the application in a browser, we see the layout with the links to the pages, and the contents of the pages are rendered in the layout.

However, as both the layout and the page files now contain the navigation, the navigation is shown twice on each page, as shown in Figure 2.

Fig 2. The navigation is shown twice as it is defined both in the layout and the page.

Fig 2. The navigation is shown twice as it is defined both in the layout and the page.

To address the issue, we can now remove the repetitive navigation from the pages. As an example, modify the +page.svelte file in the about folder to match the following.

<p>This is my about page!</p>

Once done, remove the navigation from the other pages as well. Now, when you open up the application in a browser, you see the layout with the links to the pages, and the contents of the pages are rendered in the layout.

Rendering and layouts

When creating the pages, Svelte takes the content of each +page.svelte file, and gives the content as a property children to the layout file. The content of each page is rendered to the location marked with {@render children()} in the layout file, and the rendered page — with the layout — is placed to the location that corresponds to the path of the page.

The layout file can contain other common parts of the application as well, such as a header and a footer. For example, with the following layout file, each page in the application would have a header, a navigation, the content of the page, and a footer.

<script>
  let { children } = $props();
</script>

<header>
  <h1>My application</h1>
</header>

<nav>
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/about">About</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
</nav>

<div>
  {@render children()}
</div>

<footer>
  <p>My application is cool.</p>
</footer>

Page-specific head element

Individual pages might have different titles, meta tags, and other content in the <head> element of the page. For example, the title of a page is often page-specific. In Svelte, the contents of the <head> element of the page can be defined with the svelte:head element.

To demonstrate this, modify the +page.svelte file in the about folder so that the contents of the file are as follows.

<svelte:head>
  <title>About</title>
</svelte:head>

<p>This is my about page!</p>

Now, when opening up the about page, the browser tab shows the title “About”, as shown in Figure 3. The figure also shows what the page would look like when the above layout with the header, footer, and navigation is used.

Fig 3. The browser tab shows the title "About" for the about page.

Fig 3. The browser tab shows the title “About” for the about page.

The underlying reason why this works is that SvelteKit injects the contents of svelte:head to the location marked with %sveltekit.head% in the app.html file of the src folder. If you take a peek at app.html, the file looks as follows.

<!doctype html>
<html lang="en">
	<head>
		<meta charset="utf-8" />
		<link rel="icon" href="%sveltekit.assets%/favicon.png" />
		<meta name="viewport" content="width=device-width, initial-scale=1" />
		%sveltekit.head%
	</head>
	<body data-sveltekit-preload-data="hover">
		<div style="display: contents">%sveltekit.body%</div>
	</body>
</html>

The above file also contains the %sveltekit.body% that is replaced with the contents of each page (with the layout, if a layout exists).

Loading Exercise...