To use routing in a React project we make use of a third-party package and create a file, route.js, to configure the routes. For each route created, a component file exports the component, imports it in routes.js, and configures the new route with a path property.
To avoid writing some code, Next.js adopted a file system-based routing mechanism. Every feature in Next.js has to follow some convention and in routing, when a file is added to the pages folder in a project, it automatically becomes available as a route. By mixing and matching file names with a nested folder structure it is possible to define the most common routing patterns.
Different routing patterns which will be discussed further in the article are:
Routing with pages
Nested routes
Dynamic routes
Catch-all routes
Navigate from UI
To get started, create a Next.js application using the command: npx create-next-app next-routing
.
In this article, our main focus is on routing, and therefore the folder liable for this can be the pages folder.
As we don’t have an index.js file let’s create one. This needs to be rendered when a user visits the website home page which is hosted at localhost:3000. The file should be like below with any custom message you want.
index.js
1
2
3
4
5
function Home() {
return <h1>Home Page</h1>
}
export default Home
We are going to make two more pages named ‘about’ and 'account’.
about.js
1
2
3
4
5
function Home() {
return <h1>About Page</h1>
}
export default Home
account.js
1
2
3
4
5
function Home() {
return <h1>Account Page</h1>
}
export default Home
Now if we enter localhost:3000/about we will see that the about page is rendered and the same goes for the account page.
Note: Pages are associated with a route based on their filename like the above, account.js maps to /account.
Now we want a page to be rendered when the user navigates to the URL localhost:3000/article. However, we also need a page to be rendered when the user navigates to the URL localhost:300/article/development and another for the URL localhost:300/article/competitive-programming. Now let’s see how to achieve this with the page paste routing mechanism.
Create a new page named article.js inside the pages folder.
article.js
1
2
3
4
5
function Article() {
return <h1>Article Page</h1>
}
export default Article
Users can implement nested routes by simply nesting folders inside the pages folder just make sure that the file and folder name are the same.
Now if you navigate to the URL localhost:300/article/competitive-programming you will see the desired results. You can even move the article.js file to the article folder, just rename it as index.js.
Defining routes by using predefined paths is not always enough for complex applications, which is why we will use dynamic routing to handle this. For explaining these let’s take a scenario where we are building a book store listing and detail page, so if the user navigates to the /book we should display the list of some books; however if the user navigates to /book/id we need to display details of that individual book.
Create a new folder book and a new file index.js.
index.js
1 2 3 4 5 6 7 8 9 10 11 12
function BooksList() { return ( <> <h2>Book 1</h2> <h2>Book 2</h2> <h2>Book 3</h2> <h2>Book 4</h2> </> ) } export default BooksList
In this example, we want a dynamic value that maps to a particular file in the book folder, and in Next.js we can add brackets to a page file name to create a dynamic route. In this case, the name would be [bookid].js.
[bookid].js
1
2
3
4
5
6
7
8
9
10
11
import {
useRouter
} from 'next/router'
function BookDetail() {
const router = useRouter()
const id = router.query.bookid
return <h1>Book Detail About {id} </h1>
}
export default BookDetail
Now if we navigate to the /book/3 we will see:
Let’s assume we are building a documentation site for a project. We have a couple of features and each feature has a few concepts. We want to achieve a unique route for each concept in a feature. For example, URLs like: /docs/f1/c1 or docs/f2/c1, etc.
Move to vs code and create one more folder, ‘docs’, now create a new file name […params].js. This file will match any URL that contains the docs segment in the path.
[…params].js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import {
useRouter
} from 'next/router'
function Docs() {
const router = useRouter()
const {
params = []
} = router.query
if (params.length == 2) {
return (
<h1>
Docs for feature {params[0]} and concept{params[1]}
</h1>
)
}
return <h1>Documentation Home Page</h1>
}
export default Docs
Now go ahead and navigate to URL localhost:3000/docs/f1/c1.
We can easily navigate using the link tag from the UI. Below is a code example:
next-routng/index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import Link from 'next/link'
function Home() {
return (
<div>
<h1>Home Page</h1>
<Link href='/article'>
<a>Articles</a>
</Link>
<br />
<Link href='/book'>
<a>Books</a>
</Link>
</div>
)
}
export default Home
Shallow routing enables you to alter the URL without having to re-run data fetching methods such as getServerSideProps, getStaticProps, and getInitialProps.
Without losing state, you’ll get the modified path name and query via the router object (added by useRouter or withRouter).
Set the shallow option to true to enable shallow routing. Consider the following scenario:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import {
useEffect
} from 'react'
import {
useRouter
} from 'next/router'
function Page() {
const router = useRouter()
useEffect(() => {
router.push('/?counter=10', undefined, {
shallow: true
})
}, [])
useEffect(() => {}, [router.query.counter])
}
export default Page
The URL will be changed to /?counter=10 as a result. The page is not modified; just the route’s state is altered.
You can also use componentDidUpdate to monitor URL changes, as demonstrated below:
1 2 3 4 5 6 7 8 9 10
componentDidUpdate(prevProps) { const { pathname, query } = this.props.router if (query.counter !== prevProps.router.query.counter) { // fetch data based on the new query } }
Next/link should be able to handle most of your routing needs, but you can also execute client-side navigations without it; see the next/router docs for more information.
The following example demonstrates how to use useRouter to do basic page navigation:
1
2
3
4
5
6
7
8
9
10
11
12
13
import {
useRouter
} from 'next/router'
export default function ReadMore() {
const router = useRouter()
return (
<button onClick={() => router.push('/about')}>
Click here to read more
</button>
)
}