# Dynamic Client Side Routing in Gatsby \[solved\]

**URL:** <https://forum.freecodecamp.org/t/dynamic-client-side-routing-in-gatsby-solved/400912>\
**Category:** General\
**Created:** [June 9, 2020, 3:10am UTC](https://forum.freecodecamp.org/t/dynamic-client-side-routing-in-gatsby-solved/400912 "2020-06-09T03:10:35Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![JackEdwardLyons](https://sea3.discourse-cdn.com/freecodecamp/user_avatar/forum.freecodecamp.org/jackedwardlyons/32/158616_2.png) [@JackEdwardLyons](https://forum.freecodecamp.org/u/JackEdwardLyons)\
**Post date:** [June 9, 2020, 3:10am UTC](https://forum.freecodecamp.org/t/dynamic-client-side-routing-in-gatsby-solved/400912/1 "2020-06-09T03:10:35Z")

</div>

Hi all,

I have a seemingly simple question but it’s proving tricky. In Gatsby I want to set up dynamic client-side routing like so:

`/services/:id`

So the context is, I have a `/services` page with a list of services. Then the user would click a list item to go to that service.

- service123
- service234

For example, these page would be clickable and lead to the respective `/services/service123` page.

I know there is a client-side plugin for this, I’ve tried it , but the problem is that it won’t allow me to have the root `/services` page and link dynamically off that.

I know this is a simple task, but I’m wondering why there isn’t any clear docs or demo’s on this seemingly simple process.

I’m looking at the reach router docs and can see this example, but I can’t see it being used anywhere in Gatsby? Is it OK to do it this way?

```auto
import { Match } from "@reach/router"

const App = () => (
  <Match path="/hot/:item">
    {props =>
      props.match ? (
        <div>Hot {props.match.item}</div>
      ) : (
        <div>Uncool</div>
      )
    }
  </Match>
)

```

Any help would be really appreciated,

Thank you

**SOLUTION**

Ok, I solved this in two ways:

- The server-side way (preferred)

This involved a lot more groundwork than the client-side method, but it is far superior. Basically the idea is to create a JSON file and reference it via GraphQL, generate a unique slug and loop over each JSON item slug to create a new page.

Just follow this tutorial link exactly: [https://www.gatsbyjs.org/tutorial/part-seven/](https://www.gatsbyjs.org/tutorial/part-seven/). By doing it this way you get all the benefits of Server Side Rendering (SSR), as well as querying with GraphQL, which make it a really performant and powerful approach.

- The client-side way

This was my initial way of doing things. Looking back now, it seems “hacky”. But of course there might be use cases where it makes sense. If you have a list of data on a page, you can render a `<Link />` component and point it to a wildcard route (I used the id for each mapped element). You can also pass some `state` through as well.

For example:

```auto
<Link to={`/user/${user.id}`} state={{user}}>
   {user.first_name}
</Link>

```

In the `gatsby-node` config I added this: code:

```auto
// Option One: The pages data comes from the `user-list` component.
// A Link component is passed a user id in the url (ie) `/users/${user.id}`.
// That's how the ID is rendered on the page. Additionally, state can be passed
// in with the Link component too. However, if the user refreshes the page, that data is lost.
exports.onCreatePage = async ({page, actions}) => {
  const {createPage} = actions

  if (page.path.match(/^\/user/)) {
    await createPage({
      path: '/user',
      matchPath: '/user/:id',
      component: path.resolve(`src/components/graphql/user.tsx`),
    })
  }
}

```

The major downside to this is that you don’t get all the benefits of SSR with Gatsby. I actually had to add this to my code so it would build for production:

```auto
const isSSR = typeof window === 'undefined'
  if (isSSR) {
    return <div>Loading ...</div>
  }

```

So, I would strongly recommend Option One. I hope that helps 🙂

---

<div class="post-metadata">

**Author:** ![JackEdwardLyons](https://sea3.discourse-cdn.com/freecodecamp/user_avatar/forum.freecodecamp.org/jackedwardlyons/32/158616_2.png) [@JackEdwardLyons](https://forum.freecodecamp.org/u/JackEdwardLyons)\
**Post date:** [June 9, 2020, 3:18am UTC](https://forum.freecodecamp.org/t/dynamic-client-side-routing-in-gatsby-solved/400912/2 "2020-06-09T03:18:05Z")

</div>

Would any of these solutions with Reach Router work? I will give it a try  
[https://reach.tech/router/example/nested-routes](https://reach.tech/router/example/nested-routes)

[https://reach.tech/router/api/Match](https://reach.tech/router/api/Match)

---

<div class="post-metadata">

**Author:** ![husseyexplores](https://sea3.discourse-cdn.com/freecodecamp/user_avatar/forum.freecodecamp.org/husseyexplores/32/100732_2.png) [@husseyexplores](https://forum.freecodecamp.org/u/husseyexplores)\
**Post date:** [June 9, 2020, 3:33am UTC](https://forum.freecodecamp.org/t/dynamic-client-side-routing-in-gatsby-solved/400912/3 "2020-06-09T03:33:53Z")

</div>

Did you try structuring your pages like this? Assuming your services are static, and not fetched from the database:

- src/pages/services/index.js ` - // will match '/services'`
- src/pages/services/developlement.js ` - // will match '/services/developlement'`
- src/pages/services/design.js ` - // will match '/services/design'`
- src/pages/services/consulting.js ` - // will match '/services/consulting'`

And there is no need to use router here.

But if your pages are dynamic, i.e you’re fetching these services from a database during build time, then you’d need to use the `createPage` function in `gatsby-node.js`. Just like you’d create the blogs in gatsby.

And to navigate to this, you’d use gatsby link.

```auto
import { Link } from 'gatsby'
...
<Link to="/services/design">Design</Link>

```

Clicking the above link will lead to the `design` service page.

---

<div class="post-metadata">

**Author:** ![JackEdwardLyons](https://sea3.discourse-cdn.com/freecodecamp/user_avatar/forum.freecodecamp.org/jackedwardlyons/32/158616_2.png) [@JackEdwardLyons](https://forum.freecodecamp.org/u/JackEdwardLyons)\
**Post date:** [June 9, 2020, 3:38am UTC](https://forum.freecodecamp.org/t/dynamic-client-side-routing-in-gatsby-solved/400912/4 "2020-06-09T03:38:40Z")

</div>

Yeah pages are dynamic, and I’m just trying to do a test where I map over some dummy data and go to each new page. Even when using gatsby-node, following this example:

```auto
exports.onCreatePage = async ({ page, actions }) => {
  const { createPage } = actions
  // page.matchPath is a special key that's used for matching pages
  // only on the client.
  if (page.path.match(/^\/services/)) {
    page.matchPath = "/services/*"
    // Update the page.
    createPage(page)
  }
}

```

It allows me to have a `/services` page but not a `/services` and a `/services/123` page … Not sure though

---

<div class="post-metadata">

**Author:** ![JackEdwardLyons](https://sea3.discourse-cdn.com/freecodecamp/user_avatar/forum.freecodecamp.org/jackedwardlyons/32/158616_2.png) [@JackEdwardLyons](https://forum.freecodecamp.org/u/JackEdwardLyons)\
**Post date:** [June 9, 2020, 4:12am UTC](https://forum.freecodecamp.org/t/dynamic-client-side-routing-in-gatsby-solved/400912/5 "2020-06-09T04:12:26Z")

</div>

I think I’m just trying to go against the grain with Gatsby. If I have data that I am mapping over, then I probably should try and make it queryable or at a minimum, accessible to the `gatsby-node.js` file so I can generate those pages with their subsequent `component` and pass in the props/id to that component… Not sure?

---

<div class="post-metadata">

**Author:** ![husseyexplores](https://sea3.discourse-cdn.com/freecodecamp/user_avatar/forum.freecodecamp.org/husseyexplores/32/100732_2.png) [@husseyexplores](https://forum.freecodecamp.org/u/husseyexplores)\
**Post date:** [June 9, 2020, 5:21am UTC](https://forum.freecodecamp.org/t/dynamic-client-side-routing-in-gatsby-solved/400912/6 "2020-06-09T05:21:32Z")

</div>

That’s what I was suggesting. If you have the ‘data’ to create the pages dynamically, then just create them in `gatsby-node` with ` createPage` api.  
You don’t necessarily have to make you data queryable, you can just fetch it (or import it if you have a json file, for example) and feed it to createPage.

This might help: [https://www.gatsbyjs.org/docs/using-gatsby-without-graphql/](https://www.gatsbyjs.org/docs/using-gatsby-without-graphql/)

And if you have some static data, you can just structure it as such in your pages directory.

I’ve not seen the manual routing in gatsby, especially at the whole page level, it works out of the box and there is no need to matching the routes. That’s why there are no examples of such thing in the docs.

---

<div class="post-metadata">

**Author:** ![JackEdwardLyons](https://sea3.discourse-cdn.com/freecodecamp/user_avatar/forum.freecodecamp.org/jackedwardlyons/32/158616_2.png) [@JackEdwardLyons](https://forum.freecodecamp.org/u/JackEdwardLyons)\
**Post date:** [June 9, 2020, 5:51am UTC](https://forum.freecodecamp.org/t/dynamic-client-side-routing-in-gatsby-solved/400912/7 "2020-06-09T05:51:36Z")

</div>

Cool, it’s starting to make a bit more sense now. Thanks!  
Solution added to the question above 🙂

---

<div class="post-metadata">

**Author:** ![ILM](https://sea3.discourse-cdn.com/freecodecamp/user_avatar/forum.freecodecamp.org/ilm/32/363239_2.png) [@ILM](https://forum.freecodecamp.org/u/ILM)\
**Post date:** [June 1, 2021, 6:50pm UTC](https://forum.freecodecamp.org/t/dynamic-client-side-routing-in-gatsby-solved/400912/8 "2021-06-01T18:50:19Z")

</div>

Closed on June 1, 2021, 6:50pm UTC


