Client-Side Scalability

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 the getStaticPaths is the array that is iterated over, mapped to an id, 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.

Fig. 1 -- The page at http://localhost:8000/items/3 shows the text "Information about item id 3!". The page is statically generated during build time.

Fig. 1 — The page at http://localhost:8000/items/3 shows the text “Information about item id 3!”. The page is statically generated during build time.

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.

Loading Exercise...

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>
Loading Exercise...

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.

Fig. 2 -- The page at http://localhost:8000/dynamic/books/42 is a dynamic route that is rendered on the server on demand.

Fig. 2 — The page at http://localhost:8000/dynamic/books/42 is a dynamic route that is rendered on the server on demand.
Loading Exercise...

Server-side rendering or static sites?

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.


Loading Exercise...