Pages, routes and layouts
Learning objectives
- You know how to create pages in Astro.
- You know the principle of page-based routing.
- You know how to create and use layouts.
Pages and routing
Astro is a multi-page application (MPA) framework and renders (most of) the HTML for each page on the server, building static web sites that can be simply served. Clicking on a link will lead to a page being loaded from the server, instead of loading e.g. JSON data from the server as would be the case for single-page applications (SPA). This means that the initial load time of a web application built with Astro can be faster than the initial load time of a single-page applications, as the pages have already been created on the server. On the other hand, subsequent page requests can be slower, as the amount of retrieved data may be larger.
On the other hand, it is possible to prefetch content, which mitigates the issue. For Astro, one would use the prefetch library.
Astro uses file-based routing, where each component in the pages
folder (under src
) will be created as a page. As an example, consider the following file structure (in the pages
folder).
tree --dirsfirst
.
├── secret
│ ├── index.astro
│ └── very.astro
├── about.astro
└── index.astro
With the above file structure, the following mapping would be used:
- Path
/
would lead to showingindex.astro
- Path
/about
would lead to showingabout.astro
- Path
/about/
would lead to showingabout.astro
- Path
/secret
would lead to showingsecret/index.astro
- Path
/secret/
would lead to showingsecret/index.astro
- Path
/secret/very
would lead to showingsecret/very.astro
- Path
/secret/very/
would lead to showingsecret/very.astro
That is, for example, if a user would visit the path /about
of the application, the user would be shown contents created based on the file about.astro
.
The components are not shown directly. Astro compiles the components to pages when the web application is built.
Navigation between pages is done with the HTML anchor element. For example, the following content for index.astro
(in the folder pages
) would provide links to the root path (i.e. index.astro
) and to the /about
path (i.e. about.astro
).
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Index page</title>
</head>
<body>
<h1>Index</h1>
<ul>
<li><a href="/">Index page</a></li>
<li><a href="/about">About page</a></li>
</ul>
</body>
</html>
When using the prefetch library, accessing the above page would lead to prefetching the page at /about
.
Different types of pages
The page files can be Astro components, but they can also be e.g. Markdown files, MDX files, and HTML files. For additional information, see Astro's documentation on Supported page files.
Layouts
Astro provides a mechanism for defining and creating layouts. As discussed in the Web Software Development course, layouts are used to define common parts of a user interface, which can then be used across pages. Astro layouts have a similar syntax to passing content with Svelte, where the layout has a <slot />
element, which will be replaced by the content of a page.
Create a folder layouts
under the folder src
. Add a file called Layout.astro
to the folder and add the following contents to it.
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Index page</title>
</head>
<body>
<h1>Index</h1>
<ul>
<li><a href="/">Index page</a></li>
<li><a href="/about">About page</a></li>
</ul>
<slot />
</body>
</html>
Next, create (or adjust) the index.astro
under pages
folder. Modify the file to match the following.
---
import Layout from "../layouts/Layout.astro";
---
<Layout>
<p>Hello layout!</p>
</Layout>
Now, when you access the root path of the application, you see content similar to Figure 1, showing that the application uses the layout.

When we look at the layout shown in Figure 1, there are a few issues though. With the present layout, every page would have the title Index page
and show Index
as the first level heading.
Passing properties
Layouts can be given properties, which can be used to for creating and adjusting the shown page. Layout properties are entered to the layout component. As an example, in the following, we modify the index.astro
to pass heading
and title
as properties property to the Layout
component.
---
import Layout from "../layouts/Layout.astro";
---
<Layout heading="Hello world" title="My application">
<p>Hello layout!</p>
</Layout>
The values for the properties are available in the Layout.astro
through Astro.props
. To use these values, we would enter them to the layout as follows.
<html lang="en">
<head>
<meta charset="utf-8" />
<title>{ Astro.props.title }</title>
</head>
<body>
<h1>{ Astro.props.heading }</h1>
<ul>
<li><a href="/">Index page</a></li>
<li><a href="/about">About page</a></li>
</ul>
<slot />
</body>
</html>
Now, when we open up the application again, we see that the page has changed. The page should look similar to the Figure 2, where we see the page title "My application" in the browser tab and the heading "Hello world" on the page.

Default property values
In the above example, the properties title
and heading
would have to be passed to the site. If we adjust the index.astro
back to the format where Layout
is not given any properties, the site would look as shown in Figure 3. There is no title (by default, the address is shown), and the heading is empty.

That is, above, we have removed the properties from index.astro
, which looks now as follows.
---
import Layout from "../layouts/Layout.astro";
---
<Layout>
<p>Hello layout!</p>
</Layout>
To accommodate for cases where properties are not passed, we can define default values to the layout. This is done using JavaScript in the Layout
component; we effectively check if a value exists and if not, use a default value.
In the following, we have adjusted Layout.astro
to use the values for title
and heading
from properties if they exist and if not, use the values "My epic application" and "My super heading" instead.
---
const title = Astro.props.title || "My epic title";
const heading = Astro.props.heading || "My super heading";
---
<html lang="en">
<head>
<meta charset="utf-8" />
<title>{ title }</title>
</head>
<body>
<h1>{ heading }</h1>
<ul>
<li><a href="/">Index page</a></li>
<li><a href="/about">About page</a></li>
</ul>
<slot />
</body>
</html>
Now, when generating the pages, Astro uses the layout properties from components using the layout when available, and otherwise uses the default values. As an example, for the index.astro
with no properties being passed to the Layout
, the page would look as shown in Figure 4.

Components and nested layouts
When defining layouts, it can be meaningful to divide the layout into multiple components, each with their own specific logical purpose. This is in effect the same as using Partials that were discussed in the Web Software Development course.
As an example, it could be meaningful to separate the navigation from the layout. To create a separate layout component for navigation, we would create a file called Navigation.astro
and extract the menu from Layout.astro
to it.
<ul>
<li><a href="/">Index page</a></li>
<li><a href="/about">About page</a></li>
</ul>
Now, to use the Navigation.astro
in Layout.astro
, we would import the Navigation
component and add it to the Layout
component.
---
import Navigation from "./Navigation.astro";
const title = Astro.props.title || "My epic title";
const heading = Astro.props.heading || "My super heading";
---
<html lang="en">
<head>
<meta charset="utf-8" />
<title>{ title }</title>
</head>
<body>
<h1>{ heading }</h1>
<Navigation />
<slot />
</body>
</html>
When building the application, Astro by default merges the components together, leading to a combined entity with no need to retrieve each of the components separately in the client.
The same idea can be used to create more specific layouts. As an example, if we would have an admin area -- or a secret area -- we could have a slightly modified layout for it. Create a component called SecretLayout.astro
and place it to the folder layouts
.
Now, create a folder secret
under the folder pages
, and create a file called index.astro
into the folder -- we'll use the SecretLayout
for the pages in the secret
folder.
Adjust the index.astro
in the secret
folder to match the following.
---
import SecretLayout from "../../layouts/SecretLayout.astro";
---
<SecretLayout>
<p>Hello secret layout!</p>
</SecretLayout>
Now, when we access the path /secret
, we see a page similar to the one shown in Figure 5.
