Currently I am following this example on how to redirect users in getInitialProps
https://github.com/zeit/next.js/wiki/Redirecting-in-%60getInitialProps%60
The problem is, if I want to return 404 like this, it will return a blank page instead of the usual Next.js 404 error page.
context.res.writeHead(404)
context.res.end();
It should show _error.js page when writeHead 404
Shows blank HTML document with 404 status
Put this inside your getInitialProps function
context.res.writeHead(404, {"Content-Type": "text/plain"})
context.res.end("Page not found");
context.res.finished = true
I have auto redirections depending on what URL the user inputs. Therefore I need to be able to also return 404 page depending on specific logic.
Next.js latest
next-routes
Mac OS High Sierra
Chrome latest
node latest
npm latest
No express allowed in this project FYI. I know sendStatus works in express.
See below comment instead: https://github.com/zeit/next.js/issues/3362#issuecomment-414932170
In theory you could do:
static async getInitialProps() {
const err = new Error('')
err.code = 'ENOENT'
throw err
}
This would work for SSR on next@canary. With client side navigation you'd run into all sorts of issues. So I'd recommend you to implement the error page yourself using import ErrorPage from 'next/error'
import React from 'react'
import ErrorPage from 'next/error'
export default Component => {
return class WithError extends React.Component {
static async getInitialProps(ctx) {
const props =
(Component.getInitialProps
? await Component.getInitialProps(ctx)
: null) || {}
if (props.statusCode && ctx.res) {
ctx.res.statusCode = props.statusCode
}
return props
}
render() {
if (this.props.statusCode) {
return <ErrorPage statusCode={this.props.statusCode} />
}
return <Component {...this.props} />
}
}
}
To further extend my previous answer, I wouldn't recommend throwing, instead you can use above 404 HOC if you want to render the default error page.
Usage is:
import withErrorPage from '../lib/with-error-page.js'
class SomePage extends React.Component {
static async getInitialProps() {
return {
statusCode: 404
}
}
render() {
return <div>What is rendered when there's no 404</div>
}
}
export default withErrorPage(SomePage)
If no statusCode is provided the component render
is executed. Otherwise the next/error page is rendered. If you have a custom _error.js you can import it instead of next/error.
Note that you could also provide this behavior on a global level using _app.js. In that case, you don't need a higher order component:
import App, {Container} from 'next/app'
import ErrorPage from 'next/error'
import React from 'react'
export default class MyApp extends App {
static async getInitialProps ({ Component, router, ctx }) {
let pageProps = {}
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)
}
return {pageProps}
}
render () {
const {Component, pageProps} = this.props
if (pageProps.statusCode) {
return <ErrorPage statusCode={this.props.statusCode} />
}
return <Container>
<Component {...pageProps} />
</Container>
}
}
pageProps
is the result of the page's getInitialProps
import React from 'react' import ErrorPage from 'next/error' export default Component => { return class WithError extends React.Component { static async getInitialProps(ctx) { const props = (Component.getInitialProps ? await Component.getInitialProps(ctx) : null) || {} if (props.statusCode && ctx.res) { ctx.res.statusCode = props.statusCode } return props } render() { if (this.props.statusCode) { return <ErrorPage statusCode={this.props.statusCode} /> } return <Component {...this.props} /> } } }
To further extend my previous answer, I wouldn't recommend throwing, instead you can use above 404 HOC if you want to render the default error page.
Usage is:
import withErrorPage from '../lib/with-error-page.js' class SomePage extends React.Component { static async getInitialProps() { return { statusCode: 404 } } render() { return <div>What is rendered when there's no 404</div> } } export default withErrorPage(SomePage)
If no statusCode is provided the component
render
is executed. Otherwise the next/error page is rendered. If you have a custom _error.js you can import it instead of next/error.Note that you could also provide this behavior on a global level using _app.js. In that case, you don't need a higher order component:
import App, {Container} from 'next/app' import ErrorPage from 'next/error' import React from 'react' export default class MyApp extends App { static async getInitialProps ({ Component, router, ctx }) { let pageProps = {} if (Component.getInitialProps) { pageProps = await Component.getInitialProps(ctx) } return {pageProps} } render () { const {Component, pageProps} = this.props if (pageProps.statusCode) { return <ErrorPage statusCode={this.props.statusCode} /> } return <Container> <Component {...pageProps} /> </Container> } }
pageProps
is the result of the page'sgetInitialProps
Hi @timneutkens,
we actually run a website on Next.js backed by a custom Express.js server, and we implemented your proposed solution to handle errors at the global level.
But instead of returning a plain object we tried to throw an error of type HttpError
(https://www.npmjs.com/package/http-errors) from our getInitialProps, handling it in _app.js
with a try/catch block.
Doing so we incurred in a strange behavior where:
A simple example of our implementation (the same can be found on similar threads in Stack Overflow ie: here)
class SomePage extends React.Component {
static async getInitialProps() {
// Retrieve some data from an external service
// Create and throw an error
const err = createError(404);
throw err;
}
render() {
return <div>What is rendered when there's no 404</div>
}
}
Then, in _app.js
:
import App, {Container} from 'next/app'
import ErrorPage from 'next/error'
import React from 'react'
export default class MyApp extends App {
static async getInitialProps ({ Component, router, ctx }) {
let error;
let pageProps = {};
try {
pageProps = await Component.getInitialProps(ctx);
} catch (err) {
if (err.statusCode && ctx.res) {
ctx.res.statusCode = err.statusCode;
}
error = err;
}
return { pageProps, error };
}
render () {
const { Component, pageProps, error } = this.props;
if (error && error instanceof Error) {
return <ErrorPage statusCode={error.statusCode} />
}
return (
<Container>
<Component {...pageProps} />
</Container>
);
}
}
Any idea on why this happens?
@gabrieledarrigo are you sure that client-side, your error is an instanceof Error
? Keep in mind that data (which includes what will come from this.props) is serialized using JSON.stringify
and then JSON.parse
client-side. Because of that, I'm pretty sure your error is now just a plain object.
const str = JSON.stringify({error: new Error()})
const parsed = JSON.parse(str)
parsed.error instanceof Error // false
This only works up to version next 8.0.3
but in version 9 it gives an error
Error:
Function.getInitialProps
const err = new Error()
const err = new Error()
err.code = 'ENOENT'
throw err
Most helpful comment
To further extend my previous answer, I wouldn't recommend throwing, instead you can use above 404 HOC if you want to render the default error page.
Usage is:
If no statusCode is provided the component
render
is executed. Otherwise the next/error page is rendered. If you have a custom _error.js you can import it instead of next/error.Note that you could also provide this behavior on a global level using _app.js. In that case, you don't need a higher order component:
pageProps
is the result of the page'sgetInitialProps