diff --git a/Readme.md b/Readme.md index b4a2520d982f2..b420f893643d4 100644 --- a/Readme.md +++ b/Readme.md @@ -139,6 +139,17 @@ Each top-level component receives a `url` property with the following API: - `pushTo(url)` - performs a `pushState` call that renders the new `url`. This is equivalent to following a `` - `replaceTo(url)` - performs a `replaceState` call that renders the new `url` +### Error handling + +404 or 500 errors are handled both client and server side by a default component `error.js`. If you wish to override it, define a `_error.js`: + +```jsx +import React from 'react' +export default ({ statusCode }) => ( +
An error { statusCode } occurred
+) +``` + ### Production deployment To deploy, run: @@ -169,7 +180,6 @@ For example, to deploy with `now` a `package.json` like follows is recommended: The following tasks are planned and part of our roadmap - [ ] Add option to supply a `req`, `res` handling function for custom routing -- [ ] Add option to supply a custom error component to render 404, 500 pages - [ ] Add option to extend or replace custom babel configuration - [ ] Add option to extend or replace custom webpack configuration - [ ] Investigate pluggable component-oriented rendering backends (Inferno, Preact, etc) diff --git a/lib/router.js b/lib/router.js index 6e8a95348fd5d..57e78be9428ad 100644 --- a/lib/router.js +++ b/lib/router.js @@ -149,7 +149,13 @@ export default class Router { const cancel = () => { cancelled = true } this.componentLoadCancel = cancel - const props = await (Component.getInitialProps ? Component.getInitialProps(ctx) : {}) + let props = {} + const status = ctx.xhr.status + if (status === 404 || status === 500) { + props = { statusCode: ctx.xhr.status } + } else { + props = await (Component.getInitialProps ? Component.getInitialProps(ctx) : {}) + } if (cancel === this.componentLoadCancel) { this.componentLoadCancel = null diff --git a/server/build/index.js b/server/build/index.js index 569fe63948797..1b29f9d0b769e 100644 --- a/server/build/index.js +++ b/server/build/index.js @@ -8,7 +8,7 @@ export default async function build (dir) { const templateDir = resolve(__dirname, '..', '..', 'lib', 'pages') // create `.next/pages/_error.js` - // which may be overwriten by the user sciprt, `pages/_error.js` + // which may be overwriten by the user script, `pages/_error.js` const templatPaths = await glob('**/*.js', { cwd: templateDir }) await Promise.all(templatPaths.map(async (p) => { await transpile(resolve(templateDir, p), resolve(dstDir, 'pages', p)) diff --git a/server/render.js b/server/render.js index 670fc5508ecb7..f7a020af14769 100644 --- a/server/render.js +++ b/server/render.js @@ -21,7 +21,8 @@ export async function render (url, ctx = {}, { const mod = require(p) const Component = mod.default || mod - const props = await (Component.getInitialProps ? Component.getInitialProps(ctx) : {}) + const { err, res } = ctx + const props = ctx.err ? getErrorProps(ctx, dev) : await (Component.getInitialProps ? Component.getInitialProps(ctx) : {}) const component = await read(resolve(dir, '.next', '_bundles', 'pages', path)) const { html, css } = StyleSheetServer.renderStatic(() => { @@ -62,3 +63,7 @@ export async function renderJSON (url, { dir = process.cwd() } = {}) { function getPath (url) { return parse(url || '/').pathname.slice(1).replace(/\.json$/, '') } + +function getErrorProps (ctx, dev) { + return { statusCode: ctx.res.statusCode, stacktrace: dev ? ctx.err.message : undefined } +}