visit
Disclaimer
Next.js is a dynamically developed framework and a lot of old articles about it could be outdated because Next.js made a huge leap forward. I wrote this article in May 2021 and now the current version is 10.2.3 I suggest checking while you look at examples because some tips could be outdated due to improved APIServer redirects and not found status
These features, as well as previous ones, were released in version 10 and I still see a lot of questions about how to make redirect on the server. First of all, I suggest checking if you use modern fetching methods because only they get updates and new features. I explained other reasons why to use modern fetching methods.Here is a simple way to make redirect or show page 404:// Redirect
export function getServerSideProps() {
return {
redirect: {
destination: '/',
permanent: false,
}
}
}
// Page 404
export function getServerSideProps() {
return {
notFound: true
}
}
Automatic resolving of
href
paramTry to find
next/link
in your project that has href
and as prop as well. If you found it, it's time to update that links. Because starts with 10.x version you don't need to pass both of the params you can drop href
param and rename as
to href
, and it will work well.import Link from 'next/link'
// Before
function MyComponent() {
return <Link href="/posts/[post]" as="/posts/blog-post">Blog post</Link>
}
// Since v10.x
function MyComponent() {
return <Link href="/posts/blog-post">Blog post</Link>
}
I noticed that people often use
router.push
from useRouter
. If you use it in functions that were wrapped in useCallback or just inside useEffect you have to mention it in dependencies like in the example down below.import { useEffect, useCallback } from 'react'
import { useRouter } from 'next/router'
function MyComponent() {
const { push } = useRouter()
const handleClick = useCallback(() => {
// some logic
push('/profile')
}, [push])
useEffect(() => {
if (...) {
push('/sign-in')
}
}, [push])
return <>...</>
}
import Router from 'next/router'
function MyComponent() {
const handleClick = useCallback(() => {
// some logic
Router.push('/profile')
}, [])
useEffect(() => {
if (...) {
Router.push('/sign-in')
}
}, [])
return <>...</>
}
Sometimes when I work with the router I need to get the current pathname. But react/router stores the dynamic representation of the router in that field, and it's the same as router filed. We can create these params by ourselves with
asPath
. I created a wrapper over useRouter
and always have access to the props If I need them.// lib/router.ts
import { useRouter as useNextRouter } from 'next/router'
export function useRouter() {
const router = useNextRouter()
const [pathname, queryString = ""] = router.asPath.split("?");
return Object.assign(router, { pathname, queryString });
}
import Router from 'next/router'
import { useRouter } from 'lib/router'
function MyComponent() {
const { query, pathname } = useRouter()
function handleSubmit(evt) {
const params = new FormData(evt.currentTarget).getAll("fruits");
evt.preventDefault();
Router.replace(
{ pathname, query: { ...query, params }},
null,
{ shallow: true }
)
}
return (
<form>
{...}
<button type="submit">Apply</button>
</form>
)
}
Do you wonder why the second param in
Router.replace
is null
? Since 10.x with automatic resolving, we can skip that param.Since we have such a feature as automatic resolving we can use it
next/router
, but it is a small inconvenience in its API. Because if you want to pass options
you should pass dynamic route to the first argument and full path to the second in earlier versions and Next.js kept those 3 arguments for backward compatibility. I have small handy functions that help me to use router functions easier.// lib/router.ts
import NextRouter from 'next/router'
function push(url, opts) {
return NextRouter.push(url, null, opts)
}
const Router = {
...NextRouter,
push,
}
export default Router
/* ... */
// pages/params.tsx
import Router, { useRouter } from 'lib/router'
function MyComponent() {
const { query } = useRouter()
function handleSubmit(evt) {
const params = new FormData(evt.currentTarget).getAll("fruits");
evt.preventDefault();
Router.push({ pathname, query: { ...query, params } }, { shallow: true });
}
return <>{...}</>
}
Previously published at