Client-side routing for dynamic pages

commercetools Frontend uses the Next.js router for client-side routing, making it simple to create links to any dynamic page as long as the dynamic-page-handler has the logic to handle the created links.

In this article, we'll see how to create a dynamic page and link it to other pages on the frontend.

Let's modify our Star Wars example by adding 2 new pages:

  • A static page /star-wars/films showing a list of all the Star Wars films
  • A dynamic page /star-wars/film/[filmId] showing the details of the Star Wars film with filmId

Before we get started, create a sandbox in your studio project and use this sandbox instance for the local development.

Creating the film details page

  1. Add the logic to grab the filmId from the URL pattern /star-wars/film/[filmId] so it can be used to fetch the film information from SWAPI, to the dynamic-page-handler present in the backend/index.ts file
'dynamic-page-handler': async (request: Request): Promise<DynamicPageSuccessResult | null> => {
const [_, filmId] = request.query.path.match(new RegExp('/star-wars/film/([^ /]+)'));
if (filmId) {
return await axios
.post<DynamicPageSuccessResult>('https://swapi-graphql.netlify.app/.netlify/functions/index', {
query: '{film(id:"' + filmId + '") {id, title, openingCrawl, releaseDate}}',
})
.then((response): DynamicPageSuccessResult => {
return {
dynamicPageType: 'example/star-wars-film-page',
dataSourcePayload: response.data,
pageMatchingPayload: response.data,
};
})
.catch((err) => {
return {
dynamicPageType: 'example/star-wars-film-page',
dataSourcePayload: { err },
pageMatchingPayload: { err },
};
});
}
return null;
},
Clipboard icon
  1. Create a custom Frontend component in the studio with the following schema
{
"tasticType": "example/star-wars-film",
"name": "Star wars movie",
"icon": "list",
"category": "Documentation Examples",
"schema": [
{
"name": "Configuration",
"fields": [
{
"label": "film",
"field": "film",
"type": "dataSource",
"dataSourceType": "example/star-wars-film",
"required": true
}
]
}
]
}
Clipboard icon

To do that, go to the Components area in the studio, click Create schema, paste the above JSON into the editor, then click Publish

b6c6bbc Click create schema to create a component

db9ca19 Star wars component example

  1. Create a React component in the tastics/star-wars/film/index.tsx that displays the film data passed to the page by the dynamic-page-handler
import React from 'react';
const StarWarsFilmDetails = ({ data }) => {
const film = data.data.film || {};
return (
<div>
<h2>{film.title}</h2>
<p> {film.openingCrawl}</p>
<p>Released on {film.releaseDate}</p>
</div>
);
};
export default StarWarsFilmDetails;
Clipboard icon
  1. Register StarWarsFilm component in the tastics/index.tsx file
import NotFound from './not-found';
import Markdown from './markdown/tastic';
import StarWarsFilm from './star-wars/movie';
export const tastics = {
default: NotFound,
'example/star-wars-film': StarWarsFilm,
'training/content/markdown': Markdown,
};
Clipboard icon
  1. Go to Developer then Dynamic pages in the studio, click Create schema, then paste the below JSON into the editor, and click Publish

119f8f7 Click dynamic pages in the developer area copy

{
"dynamicPageType": "example/star-wars-film-page",
"name": "Star wars film",
"category": "Documentation Example",
"icon": "stars",
"dataSourceType": "example/star-wars-film",
"isMultiple": true
}
Clipboard icon
Warning icon

You need to use the Production environment while creating the dynamic page.

  1. Go to the Dynamic pages section, select Star wars film, and create a New page version (testbed) in the Default page rule

4d5eaae 0193c70 dynamic page version2x

  1. Use the Star Wars film component on the testbed page version as shown below

border

  1. Open <http://localhost:3000/star-wars/film/ZmlsbXM6Mg==>, and the film detail page should open up as shown below

e83f752 film details page

Now that the /star-wars/film/filmId page is working, let's create the star-wars/films page to list links to all films.

Creating the films list page

You need a data source extension to fetch the list of movies from the SWAPI and a custom Frontend component to render the list of movies for the films page.

  1. Create the schema for the data source in the Data sources area in the studio
{
"customDataSourceType": "example/star-wars-all-films",
"name": "Star wars all films",
"category": "Content",
"icon": "source",
"schema": []
}
Clipboard icon

63a7c09 Click data sources in the developer area

  1. Implement the data source extension in the backend/index.ts file
'example/star-wars-all-films': async (
config: DataSourceConfiguration,
context: DataSourceContext,
): Promise<DataSourceResult> => {
return await axios
.post<DataSourceResult>('https://swapi-graphql.netlify.app/.netlify/functions/index', {
query: '{allFilms { films {id, title, episodeID}} }',
})
.then((response) => ({
dataSourcePayload: response.data,
}))
}
Clipboard icon
  1. Go to the Components area and create a schema that specifies the data source you created earlier in the schema field
{
"tasticType": "example/star-wars-all-films",
"name": "Star wars films",
"icon": "list",
"category": "Documentation Examples",
"schema": [
{
"name": "Configuration",
"fields": [
{
"label": "films",
"field": "films",
"type": "dataSource",
"dataSourceType": "example/star-wars-all-films",
"required": true
}
]
}
]
}
Clipboard icon

ca59dad Star wars component example

  1. Implement the React component in the index.ts, which renders links from the array of films provided by the data source extension
import React from 'react';
import Link from 'next/link';
const StarWarsFilms = ({ data }) => {
return (
<div className={'pt-4'}>
<h3 className={'mb-4'}>Star wars films</h3>
{data.films.dataSource.data.allFilms.films.map((film) => (
<Link key={film.id} href={`/star-wars/film/${film.id}`}>
<a className={'block underline'}>{film.title}</a>
</Link>
))}
</div>
);
};
export default StarWarsFilms;
Clipboard icon
  1. Register this component in the tastics/index.tsx file
import NotFound from './not-found';
import Markdown from './markdown/tastic';
import StarWarsFilms from './star-wars/all-films';
import StarWarsFilm from './star-wars/film';
export const tastics = {
default: NotFound,
'example/star-wars-all-films': StarWarsFilms,
'example/star-wars-film': StarWarsFilm,
'training/content/markdown': Markdown,
};
Clipboard icon
  1. Go to the Site builder, click New, then Create page folder, input a name (we're calling ours Star wars) ef95992 Go to site builder and create new page folder

  2. Select your new page folder, then click New, select Create page version, input a name (we're using films) and click Save

cab1da6 Select your page folder and create new page version

  1. Drag your component into the layout element, select the Star wars all films data source, and click Save

5cc8356 4bb658e add component static page2x

  1. Open <http://localhost:3000/star-wars/film/ZmlsbXM6Mg==>, and the film detail page should open up as shown below

films-list-page.png

Let's see the complete flow in actionExternal link icon.