In pages section we have discussed the static route and how we can implement a predefined paths in our website directly. However, defining routes by using this sort of static routes is not always enough for complex applications. Here where Next.js give that feature of adding brackets to a file name like[user].js
and simply a dynamic route will be generated.
Let's start by folowing these steps. In your terminal write down these commands: 🎹
git clone https://github.com/GSG-K3/next-js-workshop.git
- For Windows Users:
cd next-js-workshop\example\dynamic-routing
Otherwise:cd next-js-workshop/example/dynamic-routing
npm i
npm run dev
-
Inside
pages
folder, create anothermeals
folder that has[meal].js
file. By that you got this dynamic route:/meals/:meal
.Any route like
/meals/pizza
,/meals/mlukhya
, etc. will be matched bypages/meals/[meal].js
. The matched path parameter will be sent as aquery
parameter to the page. -
Import useRouter hook at the top of your
[meal].js
file, so we can get the query ("parameters of the dynamic route"). -
Create a functional component as below.
import { useRouter } from 'next/router';
const Meal = () => {
const router = useRouter();
const {meal}=router.query;
return <h1>You ain't eating "{meal}" 😏</h1>;
};
export default Meal;
PS: you can create a class Component as well, but remember you need to use withRouter instead of useRouter or wrap your class in a function component.
-
Now when you browse
http://localhost:3000/meals/pizza
you can change the meal name with whatever you want to see the effect.
Implementing dynamic routes can be applied on a file , folder or by catch all as below 👇
pages/[meals].js → /:meal (/pizza)
pages/[category]/meal.js → /:category/meal (/dessert/meal)
pages/meals/[...all].js → /meals/* (/meals/id/title/ingredients
)
Note: Predefined API routes take precedence over dynamic API routes, and dynamic API routes over catch all API.
When linking to a route with dynamic path segments you have to provide href and as to make sure the router knows which JavaScript file to load.
href
- The name of thepage
in the pages directory.as
- The url that will be shown in the browser.
In our case we have a list of meals images on home page taken from an array with a fixed data in assets/data
.
Now open /pages/index.js
file and follow the steps:
- import
Link
fromnext/link
. - Then, add a link around
img
with meal name as a url parameter like this:
<Link href='/meals/[meal]' as={`/meals/${meal.name}`}>
<img src={meal.imageUrl} />
</Link>
At the end your code in pages/index.js
should be similar to the following:
Click to view the code!
import data from '../assets/data';
import Link from 'next/link';
export default function Home() {
return (
<div
style={{
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'space-around',
}}
>
{data.map((meal) => (
<div key={meal.id}>
<Link href='/meals/[meal]' as={`/meals/${meal.name}`}>
<img src={meal.imageUrl} />
</Link>
</div>
))}
</div>
);
}
- So if you go to
http://localhost:3000/
and clicked on one of the meals images, you should redirected to the meal page with it's name on it.
next/link
should be able to cover most of your routing needs, but you can also do client-side navigations without it using next/router
.
Let's do a basic page navigation with useRouter:
- add a button inside
[meal].js
file. - use
router.push
to back to home page whenever the user clicks on the button as follows:
<button onClick={() => router.push('/')}>
Back To Home
</button>
API routes provide a straightforward solution to build your API with Next.js.
Any file inside the folderpages/api
is mapped to /api/*
and will be treated as an API endpoint instead of a page.
- For example, create an
api
folder inside pages, with a file namedhola.js
, then add the following on it:
export default (req, res) => {
res.statusCode = 200;
res.send('<h1>HOLAAA!!!</h1>');
};
Now browse http://localhost:3000/api/hola
GREAT! Now you made your first API using Next.js
- To handle different HTTP methods in an API route, you can use req.method in your request handler, like so:
export default (req, res) => {
if (req.method === 'POST') {
// Process a POST request
} else {
// Handle any other HTTP method
}
}
API routes support dynamic routes, and follow the same file naming rules used for pages.
For example, the API route pages/api/meals/[meal].js
has the following code:
export default (req, res) => {
const {
query: { meal },
} = req
res.end(`Meal Name: ${meal}`)
}
Now, a request to /api/meals/crepe
will respond with the text: Meal Name: crepe
.
As a bonus you can provide each meal with a gif & quote as you see here 😎
Hint: you can use the data inserted in /assets/data.js
file
Click to view Solution!
import { useRouter } from 'next/router';
import data from '../../assets/data';
const Meal = () => {
const router = useRouter();
// check if the meal from Query exists in data file
const result = data.filter((meal) => router.query.meal === meal.name);
return (
<div
style={{
textAlign: 'center',
}}
>
{result.length ? (
<div>
<img src={result[0].gif} />
<h2>"{result[0].quote}"</h2>
</div>
) : (
<div>
<h2>{router.query.meal}</h2>
<img src='https://media1.tenor.com/images/923340f1c1385970b953ad2a88be83fb/tenor.gif?itemid=9361819' />
</div>
)}
<button onClick={() => router.push('/')}>
Back To Home
</button>
</div>
);
};
export default Meal;