How to Create Dynamic Routes in Astro

How to Create Dynamic Routes in Astro

Generating different pages based on server-side data
Ferenc Almasi β€’ 2024 January 16 β€’ Read time 9 min read
Learn how to dynamically generate pages in Astro using its file-based routing system.

Routes in applications are essential for defining the structure of navigation, enabling users to access specific pages or functionalities seamlessly, which, in turn, enhances user experience and engagement.

Well-designed routes also facilitate efficient code organization and maintainability, making it easier for developers to manage and update the application as it grows in complexity. In this tutorial, we'll take a quick look at how Astro handles routing with its file-based approach.


How Routing Works

Astro uses a file-based routing system to generate URLs at build time. This means that URLs will be generated based on the file's name. To create routes in Astro, all route files must be created inside the src/pages folder. Let's see a couple of examples of how different files will generate different routes. Take the following table as an example:

Route fileGenerated URL
src/pages/index.astroexample.com
src/pages/about.astroexample.com/about
src/pages/about/index.astroexample.com/about
src/pages/about/webtips.astroexample.com/about/webtips

Each file in the src/pages folder specifies a route. When using index.astro directly inside src/pages, we can generate the home page. When index.astro is used inside a subfolder, for example /about, it'll generate a URL with the subfolder's name. Note that in this case, we can either use a file-based or directory-based approach, as both about.astro and about/index.astro will match the same URL.


Generating Dynamic Routes

Astro is also capable of generating dynamic routes with custom file names. This way, we can use a single file to generate many pages with different types of data. For example, to generate posts using a single file, we can create a file inside the pages folder called [post].astro. This syntax informs Astro that the file is a dynamic route. But how do we define the URLs this way? Let's see an example:

Copied to clipboard! Playground
---
export const getStaticPaths = () => {
    return [
        { params: { post: 'intro' }},
        { params: { post: 'tutorial' }},
        { params: { post: 'conclusion' }}
    ]
}

const { post } = Astro.params
---

<h1>{post}</h1>
[post].astro
Generating dynamic routes in Astro

Here we exported a function specific to Astro called getStaticPaths. This function must return an array of objects with a params property. The params.post property defines the URL of the generated page, meaning we'll have three generated routes:

  1. /post/intro
  2. /post/tutorial
  3. /post/conclusion

We can grab parameters generated through the getStaticPaths function using Astro.params. Here we display the post parameter in an h1, whose value will either be intro, tutorial, or conclusion.

The property inside the params object must always be named after the file. For example, if we have a file called [blog].astro, the property must also be called blog. Note how the property name changes based on the name of the file:

Copied to clipboard! Playground
// Using [post].astro as the file name:
export const getStaticPaths = () => {
    return [
        { params: { post: 'intro' }},
        { params: { post: 'tutorial' }},
        { params: { post: 'conclusion' }}
    ]
}

// Using [blog].astro as the file name:
export const getStaticPaths = () => {
    return [
        { params: { blog: 'intro' }},
        { params: { blog: 'tutorial' }},
        { params: { blog: 'conclusion' }}
    ]
}
The property must always match the name of the file
Looking to improve your skills? Master Astro + SEO from start to finish.
info Remove ads

Dynamic Nested Routes

This will work for most cases; however, it's not functional with nested URLs. For example, we might need to have the following URL structure on our website:

Copied to clipboard!
β”œβ”€ post/astro/intro
β”œβ”€ post/astro/tutorial
β”œβ”€ post/astro/conclusion
Nested URLs won't work with [post].astro

To support nested URLs, we need to include rest parameters in the file name to get Astro to match file paths of any depth. This can be achieved by simply renaming the file, in our case from [post].astro to [...post].astro, and updating the URLs in the getStaticPaths function:

Copied to clipboard! Playground
---
export const getStaticPaths = () => {
    return [
        { params: { post: 'astro/intro' }},
        { params: { post: 'astro/tutorial' }},
        { params: { post: 'astro/conclusion' }}
    ]
}

const { post } = Astro.params
---

<h1>{post}</h1>
[...post].astro
Defining nested routes in Astro

The ... in the file name indicates that any number of depths can be used in the URL in this route. This way, we can dynamically generate nested routes with a single file. This approach is especially useful when we can have any URL, but want to use the same template for all pages.

Note that the URLs inside the getStaticPaths function doesn't have a forward or trailing slash.

Using this approach, we can also generate the homepage by simply including an object in the array where the property is undefined:

Copied to clipboard! Playground
---
export const getStaticPaths = () => {
    return [
        { params: { route: undefined }},
        { params: { route: 'astro/intro' }},
        { params: { route: 'astro/tutorial' }},
        { params: { route: 'astro/conclusion' }}
    ]
}

const { route } = Astro.params
---

<h1>{route || 'Home'}</h1>
[...route].astro
Dynamically generating the homepage

How to Use Props in Dynamic Routes

When working with dynamic routes, in most cases we'll need to pass data alongside the URL. To achieve this, we can use the props object. Take a look at the following example:

Copied to clipboard! Playground
---
export const getStaticPaths = () => {
    const posts = [
        {
            url: 'intro',
            title: 'Introduction to Astro'
        },
        {
            url: 'conclusion',
            title: 'Conclusion'
        }
    ]

    return posts.map(post => ({
        params: { post: post.url },
        props: {
            ...post
        }
    }))
}

const { title } = Astro.props
---

<h1>{title}</h1>
[...post].astro
Using props in dynamic routes

Here we used a map to generate the necessary array of objects from the posts array. We can inject the entire post object as a prop using the spread operator. Now we can access the properties of the object through Astro.props, just like we would access props in a regular Astro component.


Route Priority

Since we can have multiple dynamic routes in the same folder, it's possible for multiple routes to match the same URL. For example, given the following folder structure, all files can match for /post/intro:

Copied to clipboard!
src/pages/post
β”œβ”€ intro.astro
β”œβ”€ [post].astro
β”œβ”€ [...postID].astro
Multiple routes matching the same URL

Because of this, Astro uses a priority order to decide which file should be used for the path when building the project. It follows the following priority order:

    1. Use static routes without path parameters
    2. Use dynamic routes with named parameters
    3. Use pre-rendered dynamic routes
    4. Use routes with rest parameters

    Endpoints always take precendence over pages.

    Given this priority order, based on the above example, we can conclude that the /post/intro URL will be matched by intro.astro. Any other URLs (such as /post/conclusion) will be matched by [post].astro, while nested URLs will be matched by [...postID].astro, e.g., /post/tutorial/intro.


    How to Handle Redirects

    Astro is also capable of handling both static and dynamic redirects. We can define redirects in our astro.config.mjs file using the redirects property:

    Copied to clipboard! Playground
    import { defineConfig } from 'astro/config'
    
    export default defineConfig({
        redirects: {
            '/from': '/to',
            '/intro': '/introduction',
            '/posts/[...post]': '/blog/[...post]'
        }
    })
    astro.config.mjs
    Defining redirects in Astro

    Note that we can also bulk redirect dynamic routes as long as the same parameters are used. By default, Astro will generate these pages with a meta refresh tag, which will automatically redirect the pages to the new URL when someone navigates to the old page.

    When an adapter is used, it'll write the necessary configuration file instead. For example, if the project is hosted on Netlify, and Netlify is configured in Astro, it'll create a _redirects file with the correct syntax. When using SSR, we can also specify the status code in the configuration file:

    Copied to clipboard! Playground
    import { defineConfig } from 'astro/config'
    
    export default defineConfig({
        redirects: {
            '/from': {
                destination: '/to',
                status: 302
            }
        }
    })
    astro.config.mjs
    301 is the default status code for redirects in Astro

    In this case, we need to assign an object to the URL that we want to redirect and use the destination property for the new URL. Alongside this, we can pass the status property to set the HTTP response status.

    Looking to improve your skills? Master Astro + SEO from start to finish.
    info Remove ads

    Excluding Pages

    Last but not least, we can also exclude pages from being built by prefixing them with an underscore (_). This is useful for temporarily disabling pages, or to group styles, tests, or utility functions together in the same folder. Take the following folder structure as an example:

    Copied to clipboard!
    src/pages/post
    β”œβ”€ [...post].astro
    β”œβ”€ _postController.js
    β”œβ”€ _post.scss
    Excluding pages in Astro

    In this example, the _postController.js and _post.scss files will be ignored and only [...post].astro will be taken into consideration. Of course, we can also disable .astro files using the same approach.


    Conclusion

    In summary, Astro's file-based routing comes with many built-in functionalities that enable us to generate routes in many possible different configurations. It's capable of handling both static and dynamic routes, handling redirects, and we can also exclude pages. It comes with a robust priority order to decide which file to use when multiple files can match the same URL.

    Do you have any experience with Astro's file-based routing? Leave your thoughts in the comments below on how you feel about using it in the framework. Thank you for reading through; happy coding!  

    Did you find this page helpful?
    πŸ“š More Webtips
    Frontend Course Dashboard
    Master the Art of Frontend
    • check Access 100+ interactive lessons
    • check Unlimited access to hundreds of tutorials
    • check Prepare for technical interviews
    Become a Pro

    Courses

    Recommended

    This site uses cookies We use cookies to understand visitors and create a better experience for you. By clicking on "Accept", you accept its use. To find out more, please see our privacy policy.