[ch4109] Closes https://github.com/vercel/next.js/issues/14500 Our current docs for `next/router` use examples from both `useRouter` and `import Router from 'next/router'`, with this PR I'm unifying the API into `useRouter` (`withRouter` is still going to be mentioned multiple times as it is the HOC alternative) and no longer mentioning that you can import a global `Router` object from `next/router`, not sure if I should mention it at least once but that didn't seem to be required. I also did some structural changes to the docs for `next/router`, now every method starts with a description, then the implementation and explanation of the parameters of the method, and then the usage example, because every method uses the same `Usage` title the hash for them would be something like `#usage`, `#usage-1`, `#usage-2`, e.t.c, so I'm not very happy with this but it looks good. Feedback wanted 🙏
12 KiB
description |
---|
Learn more about the API of the Next.js Router, and access the router instance in your page with the useRouter hook. |
next/router
Before moving forward, we recommend you to read Routing Introduction first.
useRouter
If you want to access the router
object inside any function component in your app, you can use the useRouter
hook, take a look at the following example:
import { useRouter } from 'next/router'
function ActiveLink({ children, href }) {
const router = useRouter()
const style = {
marginRight: 10,
color: router.pathname === href ? 'red' : 'black',
}
const handleClick = (e) => {
e.preventDefault()
router.push(href)
}
return (
<a href={href} onClick={handleClick} style={style}>
{children}
</a>
)
}
export default ActiveLink
useRouter
is a React Hook, meaning it cannot be used with classes. You can either use withRouter or wrap your class in a function component.
router
object
The following is the definition of the router
object returned by both useRouter
and withRouter
:
pathname
:String
- Current route. That is the path of the page in/pages
query
:Object
- The query string parsed to an object. It will be an empty object during prerendering if the page doesn't have data fetching requirements. Defaults to{}
asPath
:String
- Actual path (including the query) shown in the browser
Additionally, the following methods are also included inside router
:
router.push
Examples
Handles client-side transitions, this method is useful for cases where next/link
is not enough.
router.push(url, as, options)
url
- The URL to navigate to. This is usually the name of apage
as
- Optional decorator for the URL that will be shown in the browser. Defaults tourl
options
- Optional object with the following configuration options:shallow
: Update the path of the current page without rerunninggetStaticProps
,getServerSideProps
orgetInitialProps
. Defaults tofalse
You don't need to use
router.push
for external URLs. window.location is better suited for those cases.
Usage
Navigating to pages/about.js
, which is a predefined route:
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return <span onClick={() => router.push('/about')}>Click me</span>
}
Navigating pages/post/[pid].js
, which is a dynamic route:
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<span onClick={() => router.push('/post/[pid]', '/post/abc')}>
Click me
</span>
)
}
Redirecting the user to pages/login.js
, useful for pages behind authentication:
import { useEffect } from 'react'
import { useRouter } from 'next/router'
// Here you would fetch and return the user
const useUser = () => ({ user: null, loading: false })
export default function Page() {
const { user, loading } = useUser()
const router = useRouter()
useEffect(() => {
if (!(user || loading)) {
router.push('/login')
}
}, [user, loading])
return <p>Redirecting...</p>
}
With URL object
You can use an URL object in the same way you can use it for next/link
. Works for both the url
and as
parameters:
import { useRouter } from 'next/router'
export default function ReadMore() {
const router = useRouter()
return (
<span
onClick={() => {
router.push({
pathname: '/about',
query: { name: 'Vercel' },
})
}}
>
Click here to read more
</span>
)
}
router.replace
Similar to the replace
prop in next/link
, router.replace
will prevent adding a new URL entry into the history
stack.
router.replace(url, as, options)
- The API for
router.replace
is exactly the same as the API forrouter.push
.
Usage
Take a look at the following example:
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return <span onClick={() => router.replace('/home')}>Click me</span>
}
router.prefetch
Prefetch pages for faster client-side transitions. This method is only useful for navigations without next/link
, as next/link
takes care of prefetching pages automatically.
This is a production only feature. Next.js doesn't prefetch pages on development.
router.prefetch(url, as)
url
- The path to apage
inside thepages
directoryas
- Optional decorator forurl
, used to prefetch dynamic routes. Defaults tourl
Usage
Let's say you have a login page, and after a login, you redirect the user to the dashboard. For that case, we can prefetch the dashboard to make a faster transition, like in the following example:
import { useCallback, useEffect } from 'react'
import { useRouter } from 'next/router'
export default function Login() {
const router = useRouter()
const handleSubmit = useCallback((e) => {
e.preventDefault()
fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
/* Form data */
}),
}).then((res) => {
// Do a fast client-side transition to the already prefetched dashboard page
if (res.ok) router.push('/dashboard')
})
}, [])
useEffect(() => {
// Prefetch the dashboard page as the user will go there after the login
router.prefetch('/dashboard')
}, [])
return (
<form onSubmit={handleSubmit}>
{/* Form fields */}
<button type="submit">Login</button>
</form>
)
}
router.beforePopState
In some cases (for example, if using a Custom Server), you may wish to listen to popstate and do something before the router acts on it.
router.beforePopState(cb)
cb
- The function to run on incomingpopstate
events. The function receives the state of the event as an object with the following props:url
:String
- the route for the new state. This is usually the name of apage
as
:String
- the url that will be shown in the browseroptions
:Object
- Additional options sent by router.push
If cb
returns false
, the Next.js router will not handle popstate
, and you'll be responsible for handling it in that case. See Disabling file-system routing.
Usage
You could use beforePopState
to manipulate the request, or force a SSR refresh, as in the following example:
import { useEffect } from 'react'
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
useEffect(() => {
router.beforePopState(({ url, as, options }) => {
// I only want to allow these two routes!
if (as !== '/' && as !== '/other') {
// Have SSR render bad routes as a 404.
window.location.href = as
return false
}
return true
})
}, [])
return <p>Welcome to the page</p>
}
router.back
Navigate back in history. Equivalent to clicking the browser’s back button. It executes window.history.back()
.
Usage
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return <span onClick={() => router.back()}>Click here to go back</span>
}
router.reload
Reload the current URL. Equivalent to clicking the browser’s refresh button. It executes window.location.reload()
.
Usage
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return <span onClick={() => router.reload()}>Click here to reload</span>
}
router.events
Examples
You can listen to different events happening inside the Next.js Router. Here's a list of supported events:
routeChangeStart(url)
- Fires when a route starts to changerouteChangeComplete(url)
- Fires when a route changed completelyrouteChangeError(err, url)
- Fires when there's an error when changing routes, or a route load is cancellederr.cancelled
- Indicates if the navigation was cancelled
beforeHistoryChange(url)
- Fires just before changing the browser's historyhashChangeStart(url)
- Fires when the hash will change but not the pagehashChangeComplete(url)
- Fires when the hash has changed but not the page
Here
url
is the URL shown in the browser. If you callrouter.push(url, as)
(or similar), then the value ofurl
will beas
.
Usage
For example, to listen to the router event routeChangeStart
, open or create pages/_app.js
and subscribe to the event, like so:
import { useEffect } from 'react'
import { useRouter } from 'next/router'
export default function MyApp({ Component, pageProps }) {
const router = useRouter()
useEffect(() => {
const handleRouteChange = (url) => {
console.log('App is changing to: ', url)
}
router.events.on('routeChangeStart', handleRouteChange)
// If the component is unmounted, unsubscribe
// from the event with the `off` method:
return () => {
router.events.off('routeChangeStart', handleRouteChange)
}
}, [])
return <Component {...pageProps} />
}
We use a Custom App (
pages/_app.js
) for this example to subscribe to the event because it's not unmounted on page navigations, but you can subscribe to router events on any component in your application.
Router events should be registered when a component mounts (useEffect or componentDidMount / componentWillUnmount) or imperatively when an event happens.
If a route load is cancelled (for example, by clicking two links rapidly in succession), routeChangeError
will fire. And the passed err
will contain a cancelled
property set to true
, as in the following example:
import { useEffect } from 'react'
import { useRouter } from 'next/router'
export default function MyApp({ Component, pageProps }) {
const router = useRouter()
useEffect(() => {
const handleRouteChangeError = (err, url) => {
if (err.cancelled) {
console.log(`Route to ${url} was cancelled!`)
}
}
router.events.on('routeChangeError', handleRouteChangeError)
// If the component is unmounted, unsubscribe
// from the event with the `off` method:
return () => {
router.events.off('routeChangeError', handleRouteChangeError)
}
}, [])
return <Component {...pageProps} />
}
withRouter
If useRouter
is not the best fit for you, withRouter
can also add the same router
object to any component.
Usage
import { withRouter } from 'next/router'
function Page({ router }) {
return <p>{router.pathname}</p>
}
export default withRouter(Page)