diff --git a/docs/advanced-features/custom-server.md b/docs/advanced-features/custom-server.md index 01910f7aec1a1..f1ef1fb01c684 100644 --- a/docs/advanced-features/custom-server.md +++ b/docs/advanced-features/custom-server.md @@ -98,4 +98,4 @@ module.exports = { > Note that `useFileSystemPublicRoutes` disables filename routes from SSR; client-side routing may still access those paths. When using this option, you should guard against navigation to routes you do not want programmatically. -> You may also wish to configure the client-side Router to disallow client-side redirects to filename routes; for that refer to [`Router.beforePopState`](/docs/api-reference/next/router.md#router.beforePopState). +> You may also wish to configure the client-side router to disallow client-side redirects to filename routes; for that refer to [`router.beforePopState`](/docs/api-reference/next/router.md#router.beforePopState). diff --git a/docs/api-reference/next/router.md b/docs/api-reference/next/router.md index b22afe6137ca8..cd474b4112735 100644 --- a/docs/api-reference/next/router.md +++ b/docs/api-reference/next/router.md @@ -37,7 +37,7 @@ export default ActiveLink > `useRouter` is a [React Hook](https://reactjs.org/docs/hooks-intro.html), meaning it cannot be used with classes. You can either use [withRouter](#withRouter) or wrap your class in a function component. -### router object +## `router` object The following is the definition of the `router` object returned by both [`useRouter`](#useRouter) and [`withRouter`](#withRouter): @@ -45,27 +45,9 @@ The following is the definition of the `router` object returned by both [`useRou - `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](/docs/basic-features/data-fetching.md). Defaults to `{}` - `asPath`: `String` - Actual path (including the query) shown in the browser -Additionally, the [`Router API`](#router-api) is also included inside the object. +Additionally, the following methods are also included inside `router`: -## withRouter - -If [`useRouter`](#useRouter) is not the best fit for you, `withRouter` can also add the same [`router` object](#router-object) to any component, here's how to use it: - -```jsx -import { withRouter } from 'next/router' - -function Page({ router }) { - return

{router.pathname}

-} - -export default withRouter(Page) -``` - -## Router API - -The API of `Router`, exported by `next/router`, is defined below. - -### Router.push +### router.push
Examples @@ -77,9 +59,7 @@ The API of `Router`, exported by `next/router`, is defined below. Handles client-side transitions, this method is useful for cases where [`next/link`](/docs/api-reference/next/link.md) is not enough. ```jsx -import Router from 'next/router' - -Router.push(url, as, options) +router.push(url, as, options) ``` - `url` - The URL to navigate to. This is usually the name of a `page` @@ -87,81 +67,118 @@ Router.push(url, as, options) - `options` - Optional object with the following configuration options: - [`shallow`](/docs/routing/shallow-routing.md): Update the path of the current page without rerunning [`getStaticProps`](/docs/basic-features/data-fetching.md#getstaticprops-static-generation), [`getServerSideProps`](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering) or [`getInitialProps`](/docs/api-reference/data-fetching/getInitialProps.md). Defaults to `false` -> You don't need to use `Router` for external URLs, [window.location](https://developer.mozilla.org/en-US/docs/Web/API/Window/location) is better suited for those cases. +> You don't need to use `router.push` for external URLs. [window.location](https://developer.mozilla.org/en-US/docs/Web/API/Window/location) is better suited for those cases. #### Usage Navigating to `pages/about.js`, which is a predefined route: ```jsx -import Router from 'next/router' +import { useRouter } from 'next/router' + +export default function Page() { + const router = useRouter() -function Page() { - return Router.push('/about')}>Click me + return router.push('/about')}>Click me } ``` Navigating `pages/post/[pid].js`, which is a dynamic route: ```jsx -import Router from 'next/router' +import { useRouter } from 'next/router' + +export default function Page() { + const router = useRouter() -function Page() { return ( - Router.push('/post/[pid]', '/post/abc')}> + router.push('/post/[pid]', '/post/abc')}> Click me ) } ``` +Redirecting the user to `pages/login.js`, useful for pages behind authentication: + +```jsx +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

Redirecting...

+} +``` + #### With URL object You can use an URL object in the same way you can use it for [`next/link`](/docs/api-reference/next/link.md#with-url-object). Works for both the `url` and `as` parameters: ```jsx -import Router from 'next/router' +import { useRouter } from 'next/router' -const handler = () => { - Router.push({ - pathname: '/about', - query: { name: 'Vercel' }, - }) -} +export default function ReadMore() { + const router = useRouter() -function ReadMore() { return ( -
- Click here to read more -
+ { + router.push({ + pathname: '/about', + query: { name: 'Vercel' }, + }) + }} + > + Click here to read more + ) } +``` + +### router.replace -export default ReadMore +Similar to the `replace` prop in [`next/link`](/docs/api-reference/next/link.md), `router.replace` will prevent adding a new URL entry into the `history` stack. + +```jsx +router.replace(url, as, options) ``` -### Router.replace +- The API for `router.replace` is exactly the same as the API for [`router.push`](#router.push). + +#### Usage -Similar to the `replace` prop in [`next/link`](/docs/api-reference/next/link.md), `Router.replace` will prevent adding a new URL entry into the `history` stack, take a look at the following example: +Take a look at the following example: ```jsx -import Router from 'next/router' +import { useRouter } from 'next/router' -Router.replace('/home') -``` +export default function Page() { + const router = useRouter() -The API for `Router.replace` is exactly the same as that used for [`Router.push`](#router.push). + return router.replace('/home')}>Click me +} +``` -### Router.prefetch +### router.prefetch Prefetch pages for faster client-side transitions. This method is only useful for navigations without [`next/link`](/docs/api-reference/next/link.md), as `next/link` takes care of prefetching pages automatically. > This is a production only feature. Next.js doesn't prefetch pages on development. ```jsx -import Router from 'next/router' - -Router.prefetch(url, as) +router.prefetch(url, as) ``` - `url` - The path to a `page` inside the `pages` directory @@ -173,9 +190,10 @@ Let's say you have a login page, and after a login, you redirect the user to the ```jsx import { useCallback, useEffect } from 'react' -import Router from 'next/router' +import { useRouter } from 'next/router' export default function Login() { + const router = useRouter() const handleSubmit = useCallback((e) => { e.preventDefault() @@ -187,13 +205,13 @@ export default function Login() { }), }).then((res) => { // Do a fast client-side transition to the already prefetched dashboard page - if (res.ok) Router.push('/dashboard') + if (res.ok) router.push('/dashboard') }) }, []) useEffect(() => { // Prefetch the dashboard page as the user will go there after the login - Router.prefetch('/dashboard') + router.prefetch('/dashboard') }, []) return ( @@ -205,57 +223,82 @@ export default function Login() { } ``` -### Router.beforePopState +### router.beforePopState In some cases (for example, if using a [Custom Server](/docs/advanced-features/custom-server.md)), you may wish to listen to [popstate](https://developer.mozilla.org/en-US/docs/Web/Events/popstate) and do something before the router acts on it. -You could use this to manipulate the request, or force a SSR refresh, as in the following example: - ```jsx -import Router from 'next/router' - -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 -}) +router.beforePopState(cb) ``` -`Router.beforePopState(cb: () => boolean)` - - `cb` - The function to run on incoming `popstate` 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 a `page` - `as`: `String` - the url that will be shown in the browser - - `options`: `Object` - Additional options sent by [Router.push](#router.push) + - `options`: `Object` - Additional options sent by [router.push](#router.push) -If the function you pass into `beforePopState` returns `false`, `Router` will not handle `popstate` and you'll be responsible for handling it, in that case. See [Disabling file-system routing](/docs/advanced-features/custom-server.md#disabling-file-system-routing). +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](/docs/advanced-features/custom-server.md#disabling-file-system-routing). -### Router.back +#### Usage + +You could use `beforePopState` to manipulate the request, or force a SSR refresh, as in the following example: + +```jsx +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

Welcome to the page

+} +``` + +### router.back Navigate back in history. Equivalent to clicking the browser’s back button. It executes `window.history.back()`. +#### Usage + ```jsx -import Router from 'next/router' +import { useRouter } from 'next/router' -Router.back() +export default function Page() { + const router = useRouter() + + return router.back()}>Click here to go back +} ``` -### Router.reload +### router.reload Reload the current URL. Equivalent to clicking the browser’s refresh button. It executes `window.location.reload()`. +#### Usage + ```jsx -import Router from 'next/router' +import { useRouter } from 'next/router' + +export default function Page() { + const router = useRouter() -Router.reload() + return router.reload()}>Click here to reload +} ``` -### Router.events +### router.events
Examples @@ -264,7 +307,7 @@ Router.reload()
-You can listen to different events happening inside the Router. Here's a list of supported events: +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 change - `routeChangeComplete(url)` - Fires when a route changed completely @@ -274,53 +317,82 @@ You can listen to different events happening inside the Router. Here's a list of - `hashChangeStart(url)` - Fires when the hash will change but not the page - `hashChangeComplete(url)` - Fires when the hash has changed but not the page -> Here `url` is the URL shown in the browser. If you call `Router.push(url, as)` (or similar), then the value of `url` will be `as`. +> Here `url` is the URL shown in the browser. If you call `router.push(url, as)` (or similar), then the value of `url` will be `as`. + +#### Usage -For example, to listen to the router event `routeChangeStart`, do the following: +For example, to listen to the router event `routeChangeStart`, open or create `pages/_app.js` and subscribe to the event, like so: ```jsx -import Router from 'next/router' +import { useEffect } from 'react' +import { useRouter } from 'next/router' -const handleRouteChange = (url) => { - console.log('App is changing to: ', url) -} +export default function MyApp({ Component, pageProps }) { + const router = useRouter() -Router.events.on('routeChangeStart', handleRouteChange) -``` + useEffect(() => { + const handleRouteChange = (url) => { + console.log('App is changing to: ', url) + } -If you no longer want to listen to the event, unsubscribe with the `off` method: + router.events.on('routeChangeStart', handleRouteChange) -```jsx -import Router from 'next/router' + // If the component is unmounted, unsubscribe + // from the event with the `off` method: + return () => { + router.events.off('routeChangeStart', handleRouteChange) + } + }, []) -Router.events.off('routeChangeStart', handleRouteChange) + return +} ``` +> We use a [Custom App](/docs/advanced-features/custom-app.md) (`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](https://reactjs.org/docs/hooks-effect.html) or [componentDidMount](https://reactjs.org/docs/react-component.html#componentdidmount) / [componentWillUnmount](https://reactjs.org/docs/react-component.html#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: ```jsx -import Router from 'next/router' +import { useEffect } from 'react' +import { useRouter } from 'next/router' -Router.events.on('routeChangeError', (err, url) => { - if (err.cancelled) { - console.log(`Route to ${url} was cancelled!`) - } -}) +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 +} ``` -Router events should be registered when a component mounts ([useEffect](https://reactjs.org/docs/hooks-effect.html) or [componentDidMount](https://reactjs.org/docs/react-component.html#componentdidmount) / [componentWillUnmount](https://reactjs.org/docs/react-component.html#componentwillunmount)) or imperatively when an event happens, as in the following example: +## withRouter + +If [`useRouter`](#useRouter) is not the best fit for you, `withRouter` can also add the same [`router` object](#router-object) to any component. + +### Usage ```jsx -import Router from 'next/router' +import { withRouter } from 'next/router' -useEffect(() => { - const handleRouteChange = (url) => { - console.log('App is changing to: ', url) - } +function Page({ router }) { + return

{router.pathname}

+} - Router.events.on('routeChangeStart', handleRouteChange) - return () => { - Router.events.off('routeChangeStart', handleRouteChange) - } -}, []) +export default withRouter(Page) ``` diff --git a/docs/routing/imperatively.md b/docs/routing/imperatively.md index ba4659d889f28..7dbbf7a16a42d 100644 --- a/docs/routing/imperatively.md +++ b/docs/routing/imperatively.md @@ -1,5 +1,5 @@ --- -description: Client-side navigations are also possible using the Router API instead of the Link component. Learn more here. +description: Client-side navigations are also possible using the Next.js Router instead of the Link component. Learn more here. --- # Imperatively @@ -11,18 +11,18 @@ description: Client-side navigations are also possible using the Router API inst
-[`next/link`](/docs/api-reference/next/link.md) should be able to cover most of your routing needs, but you can also do client-side navigations without it, take a look at the [Router API documentation](/docs/api-reference/next/router.md#router-api). +[`next/link`](/docs/api-reference/next/link.md) should be able to cover most of your routing needs, but you can also do client-side navigations without it, take a look at the [documentation for `next/router`](/docs/api-reference/next/router.md). -The following example shows the basic usage of the Router API: +The following example shows how to do basic page navigations with [`useRouter`](/docs/api-reference/next/router.md#useRouter): ```jsx -import Router from 'next/router' +import { useRouter } from 'next/router' function ReadMore() { + const router = useRouter() + return ( -
- Click Router.push('/about')}>here to read more -
+ router.push('/about')}>Click here to read more ) } diff --git a/docs/routing/shallow-routing.md b/docs/routing/shallow-routing.md index 9aa21feae566b..caa9cf35138e1 100644 --- a/docs/routing/shallow-routing.md +++ b/docs/routing/shallow-routing.md @@ -38,14 +38,6 @@ function Page() { export default Page ``` -If you don't need to add the router object to the page, you can also use the [Router API](/docs/api-reference/next/router.md#router-api) directly, like so: - -```jsx -import Router from 'next/router' -// Inside your page -Router.push('/?counter=10', undefined, { shallow: true }) -``` - The URL will get updated to `/?counter=10`. and the page won't get replaced, only the state of the route is changed. You can also watch for URL changes via [`componentDidUpdate`](https://reactjs.org/docs/react-component.html#componentdidupdate) as shown below: @@ -65,7 +57,7 @@ componentDidUpdate(prevProps) { Shallow routing **only** works for same page URL changes. For example, let's assume we have another page called `pages/about.js`, and you run this: ```jsx -Router.push('/?counter=10', '/about?counter=10', { shallow: true }) +router.push('/?counter=10', '/about?counter=10', { shallow: true }) ``` Since that's a new page, it'll unload the current page, load the new one and wait for data fetching even though we asked to do shallow routing.