Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Next.js Redirect from / to another page

I'm new in Next.js and I'm wondering how to redirect from start page ( / ) to /hello-nextjs for example. Once user loads a page and after that determine if path === / redirect to /hello-nextjs

In react-router we do something like:

<Switch>
  <Route path="/hello-nextjs" exact component={HelloNextjs} />
  <Redirect to="/hello-nextjs" /> // or <Route path="/" exact render={() => <Redirect to="/hello-nextjs" />} />
</Switch>
like image 653
Arthur Avatar asked Sep 30 '19 19:09

Arthur


3 Answers

Update: Next.js >= 12.1
As @warfield pointed out in his answer from next.js >= 12.1 relative URLs are no longer allowed in redirects and using them will throw an error. I'm reposting here his answer for more visibility :

To redirect using middleware with Next.js >= 12.1:

  1. Create a middleware.ts (or .js) file at the same level as your pages directory
  2. Export a middleware function
  3. Create an absolute URL and pass it to redirect

TypeScript example middleware.ts:


import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {   
  const url = request.nextUrl.clone()   
  if (url.pathname === '/') {
    url.pathname = '/hello-nextjs'
    return NextResponse.redirect(url)   
  } 
}

Update: Next.js >= 12
Now you can do redirects using middleware, create a _middleware.js file inside the pages folder (or any sub folder inside pages)

import { NextResponse, NextRequest } from 'next/server'
export async function middleware(req, ev) {
    const { pathname } = req.nextUrl
    if (pathname == '/') {
        return NextResponse.redirect('/hello-nextjs')
    }
    return NextResponse.next()
}

Update: Next.js >= 10

From Next.js 10 you can do server side redirects (see below for client side redirects) with a redirect key inside getServerSideProps or getStaticProps :

export async function getServerSideProps(context) {
  const res = await fetch(`https://.../data`)
  const data = await res.json()
  // or use context.resolvedUrl for conditional redirect
  // if(context.resolvedUrl == "/")
  if (!data) {
    return {
      redirect: {
        destination: '/hello-nextjs',
        permanent: false,
      },
    }
  }

  return {
    props: {}, // will be passed to the page component as props
  }
}

Note : Using getServerSideProps will force the app to SSR,also redirecting at build-time is not supported , If the redirects are known at build-time you can add those inside next.config.js

In next.js you can redirect after the page is loaded using Router ex :

import Router from 'next/router'

componentDidMount(){
    const {pathname} = Router
    if(pathname == '/' ){
       Router.push('/hello-nextjs')
    }
}

Or with Hooks :

import React, { useEffect } from "react";
import Router from 'next/router'

...
useEffect(() => {
   const {pathname} = Router
   if(pathname == '/' ){
       Router.push('/hello-nextjs')
   }
 });

If you want to prevent the flashing before the redirect you can use a simple trick :

import React, { useEffect,useState } from "react";
import Router from 'next/router'
const myPage = ()=>{
    const [loaded,setLoaded] = useState(false)
    useEffect(() => {
        const {pathname} = Router
        // conditional redirect
        if(pathname == '/' ){
            // with router.push the page may be added to history
            // the browser on history back will  go back to this page and then forward again to the redirected page
            // you can prevent this behaviour using location.replace
            Router.push('/hello-nextjs')
           //location.replace("/hello-nextjs")
        }else{
            setLoaded(true)
        }
      },[]);

    if(!loaded){
        return <div></div> //show nothing or a loader
    }
    return ( 
        <p>
            You will see this page only if pathname !== "/" , <br/>
        </p> 
    )
}
export default myPage

I would say that in general is not a good/elegant approach to do client redirects when you can use next.config.js redirects or even better use conditional render of components.

I have create a simple repo with all the examples above here.

like image 67
Nico Avatar answered Nov 08 '22 12:11

Nico


Caveat

First, you should asses whether you need client-side redirection (within React), server-side redirection (301 HTTP response) or server-side redirection + authentication (301 HTTP response but also having some logic to check authentication).

This is the most complete answer I could write. But, in most scenarios, you do not need any of this. Just redirect as you would do in any React app. Prefer client-side redirections first. Just using useEffect + router.push, and that's it.

Server-side redirection are tempting, in particular when you want to "secure" private pages, but you should assess whether you really need them. Usually, you don't. They induce unexpected complexity, like managing auth token and refresh token. Instead, you may want to add a gateway server, a reverse proxy or whatever upfront server to your architecture for instance to handle those kind of checks.

Keep in mind that Next.js are just React app, and using Next.js advanced features like SSR comes at a cost that should be justified in your context.

Next 9.5 update

As stated by @Arthur in the comments, 9.5 also include the possibilities to setup redirects in next.config.js. The limitations of this feature are not yet clear to me, but they seem to be global redirections, e.g. when you need to move a page or to allow access only during a limited period. So they are not meant to handle authentication for instance, because they don't seem to have access to the request context. Again, to be confirmed.

Next 10 new doc update

This solution is specific to redirection depending on authentication.

Authentication patterns are now documented

I am not fond of authenticated from getServerSideProps, because it's in my opinion quite too late and can be difficult to set up with advanced patterns such as handling refresh token. But that's the official solution.

You may also want to check the approach documented in this ticket based on how Vercel's dashboard works (at the time of writing), that prevents flash of unauthenticated content

Next 10.2 header and cookies based rewrites update

Next 10.2 introduces Rewrites based on headers and cookies. That's a great way to redirect server-side, based on the presence of an authentication cookie or header.

However, keep in mind that this is not a secure redirection. User can alter their request headers with a false token. You still need a gateway, a reverse proxy or an upfront server to actually check token validity and correctly set the headers.

Edit: note that the URL won't change. A rewrite points an URL to an existing page of your application, without changing the URL => it allows you to have "virtual" URLs.

Example use case: imagine you have a page src/contact.tsx, that is translated, and i18n redirection setup. You can translate the page name itself ("contact") by rewriting /de/kontact to /de/contact.

Next 12 update

Now middlewares gives you full-control on server-side redirects.

However, keep in mind again, that most of the time a client-side redirect and check is just enough.


Outdated Next 9.4 answer (links are dead sorry)

Hi, here is an example component working in all scenarios:

Vulcan next starter withPrivate access

Example usage here

The answer is massive, so sorry if I somehow break SO rules, but I don't want to paste a 180 lines piece of code. There is no easy pattern to handle redirection in Next, if you want to both support SSR and static export.

The following scenarios each need a specific pattern:

  • server side rendering: we render the page if allowed, HTTP redirect if not
  • static rendering (server-side): we render nothing, but we still include the page into the build
  • client side rendering, after a static export: we check client side if the user is auth, and redirect or not. We display nothing (or a loader) during this check or if we are redirecting.
  • client side rendering after a client redirect using next/router: same behaviour.
  • client side rendering after SSR: we use props passed by getInitialProps to tell if the user is allowed, directly at first render. It's just a bit faster, you avoid a blank flash.

At the time of writing (Next 9.4), you have to use getInitialProps, not getServerSideProps, otherwise you lose the ability to do next export.

Even more outdated old answer (works, but will have a messy static render)

Semi-official example

The with-cookie-auth examples redirect in getInitialProps. I am not sure whether it's a valid pattern or not yet, but here's the code:

Profile.getInitialProps = async ctx => {
  const { token } = nextCookie(ctx)
  const apiUrl = getHost(ctx.req) + '/api/profile'

  const redirectOnError = () =>
    typeof window !== 'undefined'
      ? Router.push('/login')
      : ctx.res.writeHead(302, { Location: '/login' }).end()

  try {
    const response = await fetch(apiUrl, {
      credentials: 'include',
      headers: {
        Authorization: JSON.stringify({ token }),
      },
    })

    if (response.ok) {
      const js = await response.json()
      console.log('js', js)
      return js
    } else {
      // https://github.com/developit/unfetch#caveats
      return await redirectOnError()
    }
  } catch (error) {
    // Implementation or Network error
    return redirectOnError()
  }
}

It handles both server side and client side. The fetch call is the one that actually get the auth token, you might want to encapsulate this into a separate function.

What I would advise instead

 1. Redirect on server-side render (avoid flash during SSR)

This is the most common case. You want to redirect at this point to avoid the initial page flashing on first load.

MyApp.getInitialProps = async appContext => {
    const currentUser = await getCurrentUser(); // define this beforehand
    const appProps = await App.getInitialProps(appContext);
    // check that we are in SSR mode (NOT static and NOT client-side)
    if (typeof window === "undefined" && appContext.ctx.res.writeHead) {
      if (!currentUser && !isPublicRoute(appContext.router.pathname)) {
          appContext.ctx.res.writeHead(302, { Location: "/account/login" });
          appContext.ctx.res.end();
      }
    }
    return { ...appProps, currentUser };
  };

 2. Redirect in componentDidMount (useful when SSR is disabled, eg in static mode)

This is a fallback for client side rendering.

  componentDidMount() {
    const { currentUser, router } = this.props;
    if (!currentUser && !isPublicRoute(router.pathname)) {
      Router.push("/account/login");
    }
  }

I could not avoid flashing the initial page in static mode add this point, because you can't redirect during the static build, but it seems better than the usual approaches. I'll try to edit as I make progress.

Full example is here

Relevant issue, which sadly ends up with a client only answer

New issue I've opened regarding redirecton

like image 21
Eric Burel Avatar answered Nov 08 '22 11:11

Eric Burel


There are three approaches.

1.Redirect on events or functions:

import Router from 'next/router';

<button type="button" onClick={() => Router.push('/myroute')} />

2.Redirect with hooks:

import Router , {useRouter}  from 'next/router';

const router = useRouter()

<button type="button" onClick={() => router.push('/myroute')} />

3.Redirect with Link:

based on Nextjs docs the <a> tag is neccessary inside the link for things like open in a new tab!

import Link from 'next/link';

<Link href="/myroute">
   <a>myroute</a>
</Link>

There are some other options for serverside routing which is asPath. in all described approaches you can add asPath to redirect both client and server side.

like image 52
Afsanefda Avatar answered Nov 08 '22 12:11

Afsanefda