Astro Routes and Rendering
Learning Objectives
- You know how to use dynamic routes to create statically generated pages.
- You know how to use server-side rendering to render pages on-demand.
Previously, we learned that all files in the src/pages
folder correspond to a specific route in the Astro application. By default, Astro uses static site generation, where the HTML for each of the pages is generated during build time.
Dynamic routes
There are, however, scenarions where it does not make sense to create individual files for each route. For example, when we have a large number of pages that are similar in structure, such as a blog with many posts, it is not practical to create a separate file for each post. In such cases, we can use dynamic routes to generate content on-demand.
Dynamic routes are defined by creating a folder with the name of the dynamic route, and the name of the file in the folder is enclosed in square brackets. For example, to create a dynamic route for items, we can create a folder named src/pages/items
, and add a file called [id].astro
to the folder. The file [id].astro
can then be used to render the content for the dynamic route.
For rendering content, Astro has a getStaticPaths
function that must be exported from the dynamic route component. This function returns an array of paths that will be generated during the build time. The paths are defined as objects with a params
property that contains the parameters for the dynamic route. For example, to generate pages for items with ids 1, 2, and 3, the getStaticPaths
function can be defined as follows:
---
export const getStaticPaths = () => {
return [
{ params: { id: 1 } },
{ params: { id: 2 } },
{ params: { id: 3 } },
{ params: { id: 4 } },
];
}
---
<!-- Template -->
With such a function in the [id].astro
file, Astro will generate pages for the paths /items/1
, /items/2
, /items/3
, and /items/4
during the build time. The concrete rendering of the files is defined after the getStaticPaths
function in the same file, where the id
parameter can be accessed as Astro.params.id
in the component.
---
export const getStaticPaths = () => {
return [
{ params: { id: 1 } },
{ params: { id: 2 } },
{ params: { id: 3 } },
{ params: { id: 4 } },
];
}
const id = Astro.params.id;
---
<!-- Template -->
You can think of the above as a mapping function from the array returned from the
getStaticPaths
function to the actual content that will be rendered for each path, where thegetStaticPaths
is the array that is iterated over, mapped to anid
, which is then used to render the content for the path.
To concretely define what each page holds, we can render the content as we would do with any Astro component. The following shows an example, where each renrered page would be rendered with the layout, and each page would have the title “Items” and a paragraph with information about the item with the corresponding id.
---
export const getStaticPaths = () => {
return [
{ params: { id: 1 } },
{ params: { id: 2 } },
{ params: { id: 3 } },
{ params: { id: 4 } },
];
}
import Layout from "../../layouts/Layout.astro";
const id = Astro.params.id;
---
<Layout title="Items">
<p>Information about item id { id }!</p>
</Layout>
Now, the pages at paths /items/1
, /items/2
, /items/3
, and /items/4
will be generated during the build time, and the content will be rendered as defined in the [id].astro
file. As an example, the page at http://localhost:8000/items/3
looks similar to the one shown in Figure 1 below.
Note that Astro treats parameters as strings by default, so if you need to use the parameter as a number, you need to convert it to a number. As an example, the following multiplication page would not work as expected:
---
export const getStaticPaths = () => {
return [
{ params: { id: 1 } },
{ params: { id: 2 } },
{ params: { id: 3 } },
{ params: { id: 4 } },
];
}
import Layout from "../../layouts/Layout.astro";
const id = Astro.params.id;
const result = id * 2;
---
<Layout title="Items">
<p>{ id } * 2 = { result }</p>
</Layout>
Although we use a simple example above where the data is predefined, the data could as well be retrieved for example from an API or a database. The
getStaticPaths
function can be used to fetch the data and return the paths based on the data fetched.
Server-side rendering
Astro supports also server-side rendering (SSR) for pages that require dynamic behavior. To enable server-side rendering, an adapter needs to be configured for the Astro application. The adapter is responsible for handling the server-side rendering and serving the pages on-demand.
For example, to configure a Deno adapter for Astro, we would use the deno-astro-adapter. To add the adapter, we first need to install it to the client project — in the client
folder, run the following command:
deno install npm:@deno/astro-adapter
This adds the adapter to the project. Then, we need to configure it for the project. Astro has a file astro.config.mjs
that contains the configuration for the Astro application. Modify the file to match the following:
import { defineConfig } from 'astro/config';
import deno from "@deno/astro-adapter";
export default defineConfig({
adapter: deno(),
});
Now, we can create pages that are rendered on-demand using server-side rendering. The pages can be created in the same way as the static pages, but the content is rendered on-demand when a request is made to the page.
By default, all pages continue to be statically generated.
To mark a page as a page that is rendered dynamically, we add the line export const prerender = false;
to the component definition. For example, let’s create a new file src/pages/dynamic-demo.astro
with the following content.
---
export const prerender = false;
---
<p>This page is rendered on-demand!</p>
Now, the page is at the path http://localhost:8000/dynamic-demo
is rendered on-demand when a request is made to the page. That is, it is not pre-generated during the build time, but generated when a request is made to the page. To change it so that it is pre-generated, we can either drop the line export const prerender = false;
or change it to export const prerender = true;
.
As an example, if the page is as follows, the page at http://localhost:8000/dynamic-demo
will be pre-generated during the build time.
---
export const prerender = true;
---
<p>This page is statically generated during build time!</p>
Dynamic routes and server-side rendering
With a server-side rendering adapter configured, we can combine dynamic routes with server-side rendering to create pages that are generated on-demand. This allows us to have a mix of static and dynamic pages in the Astro application, where the majority of the pages are statically generated, but specific pages can be rendered on-demand when needed.
To create a dynamic route that is rendered on-demand, we can use the same approach as with the static pages, but mark the page as not prerendered. For example, to create a dynamic route for items that are rendered on-demand, we can create a folder named src/pages/dynamic
, and add a file called [id].astro
to the folder. The file can then be defined as follows:
---
export const prerender = false;
---
<p>This is a dynamic route with id {Astro.params.id}</p>
Now, the page at the path http://localhost:8000/dynamic/1
is rendered on-demand when a request is made to the page. The content is generated when the request is made, and the page is not pre-generated during the build time.
curl localhost:8000/dynamic/42
// ...
<p data-astro-source-file="/app/src/pages/dynamic/[id].astro"
data-astro-source-loc="7:4">
42
</p>
// ...
The same idea works for nested dynamic routes, where the folder structure is nested to match the dynamic route structure. For example, to create a nested dynamic route for items with categories, we can create a folder called [category]
to the folder src/pages/dynamic
, and then add a file called [id].astro
to the [category]
folder.
When defining the content for the nested dynamic route, we can access the parameters for the dynamic route using Astro.params.category
and Astro.params.id
. For example, the content for the nested dynamic route can be defined as follows:
---
export const prerender = false;
---
<p>Category: {Astro.params.category}, id: {Astro.params.id}</p>
Figure 2 below shows the result of accessing the route http://localhost:8000/dynamic/books/42
.
Serving pages that have been statically generated during build time is more efficient than serving the same sites with server-side rendering. However, server-side rendering can be useful for pages that require dynamic behavior, such as personalized content or real-time data. When deciding between server-side rendering and static site generation, consider the trade-offs between performance and flexibility.