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?
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/. 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:
<Link to={`/user/${user.id}`} state={{user}}>
{user.first_name}
</Link>
In the gatsby-node
config I added this: code:
// 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:
const isSSR = typeof window === 'undefined'
if (isSSR) {
return <div>Loading ...</div>
}
So, I would strongly recommend Option One. I hope that helps