Next.js is a popular React framework that enables us to build server-side rendered and statically generated web applications. One of the core features of Next js is client-side routing and navigation by using the <Link>
component from next/link
.
[lwptoc]
But in some cases, instead of navigating by using a <Link>, we need to navigate programmatically in our application for example in a menu or sidebar navigation, on form submissions, pagination, or login/logout flows etc.
In such situations, we can use the withRouter higher-order components.
withRouter gives components access to the router prop so they can use imperative navigation methods like
push()
andreplace()
.
When to Use withRouter
In Next.js, by default, only page components get access to the router
prop automatically. Whereas Non-page components like headers, footers, and menus do not have routing capabilities. Following are some of the non-page components where we may use withRouter
HOC:
- Login and logout flows in a header or sidebar
- Category filters that navigate to different product pages
- Pagination navigation
- Breadcrumb or multi-level navigation
- Back/forward browser history buttons
withRouter allows us to easily add routing context to any component, which enables us to navigate imperatively like a page.
For using it, we wrap a component export with the withRouter
higher-order component which give it access to history
, location
, match
and other routing props.
import { withRouter } from 'next/router'
function MyComponent({ history }) {
function handleLogin() {
history.push('/login')
}
return <button onClick={handleLogin}>Login</button>
}
export default withRouter(MyComponent)
This approach proves cleaner than passing down the router prop from every parent component just to enable routing in a child.
How withRouter
Works
Under the hood, withRouter
is a higher-order component (HOC) that takes a React Component as input and returns a Component having additional routing props.
It basically works by extracting the router object from the React context which is provided by Next.js and  _app.js
file creates this context which wraps all pages.
The withRouter
simply accesses this context internally and passes down the history
, location
, match
and other props to the wrapped component as already mentioned. As a result, we get access to routing capabilities outside of <Link>
and page components. We can do similar Router enablement to any component in our app.
How to Use withRouter
?
We can start by importing the withRouter
from ‘next/router’:
import { withRouter } from 'next/router'
Then we need to wrap the exported component with the withRouter
HOC:
function MyComponent({ history }) {
return <div>My Component</div>
}
export default withRouter(MyComponent)
Now MyComponent
will receive the same props as a page component – history
, location
, match
 etc.
Thereafter, we can use the history
prop to navigate in the app:
history.push('/new-page')
history.replace('/new-page')
The component will not become a page, but it can navigate like one.
Alternative to Link
We can easily deploy withRouter
as an alternative to <Link>
in cases where you need to navigate programmatically.
For example, in a sidebar navigation:
<Sidebar>
<MenuItem onClick={() => history.push('/settings')}>Settings</MenuItem>
</Sidebar>
Or for a pagination component:
function Pagination() {
const handleNext = () => {
history.push(`/products?page=${nextPage}`)
}
// ...
}
export default withRouter(Pagination)
So, by using withRouter
allows routing capabilities in components other than pages.
How to Access Route Params?
Now, let’s see how we can access route params directly from the other props added by withRouter
:
function Nav() {
const { pathname, query, asPath } = props;
// ...
}
export default withRouter(Nav)
This will allow us to conditionally show elements based on the current route.
Moreover, withRouter
keeps routing logic inside the wrapped component itself, instead of any need to pass callbacks down from parents which helps to decouple and isolate concerns.
Limitations for Using withRouter
The withRouter
HOC has very useful features, but it also comes with a few limitations, which need to be discussed and kept in mind while using it:
- When a parent re-renders, it can cause extra re-renders on the wrapped component which can cause extra load. So you can consider using React context if you need to avoid this.
- Components become less reusable if they have routing logic embedded with
withRouter
. - We can still pass routing props from parents manually instead of using withRouter. So only use it when you need the convenience.
So we can say that withRouter
is a useful tool for certain situations but also has drawbacks. It should be used after doing a quick maths on priorities for navigation components, and avoid its overuse.
Leave a Reply