diff --git a/README.md b/README.md index cadddb53..290caa14 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ and add a script to your package.json like this: { "scripts": { "dev": "next" - } + } } ``` @@ -142,9 +142,9 @@ For the initial page load, `getInitialProps` will execute on the server only. `g - `xhr` - XMLHttpRequest object (client only) - `err` - Error object if any error is encountered during the rendering -### Routing +### Routing with -Client-side transitions between routes are enabled via a `` component +Client-side transitions between routes can be enabled via a `` component #### pages/index.js @@ -178,11 +178,54 @@ 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` +### Routing with next/router + +You can also do client-side page transitions using the `next/router`. This is the same API used inside the above `` component. + +```jsx +import Router from 'next/router' + +const routeTo(href) { + return (e) => { + e.preventDefault() + Router.push(href) + } +} + +export default () => ( +
Welcome to About!
+) +``` + +Above `Router` object comes with the following API: + +- `route` - `String` of the current route +- `pathname` - `String` of the current path excluding the query string +- `query` - `Object` with the parsed query string. Defaults to `{}` +- `push(url, pathname=url)` - performs a `pushState` call associated with the current component +- `replace(url, pathname=url)` - performs a `replaceState` call associated with the current component + +> Usually, route is the same as pathname. +> But when used with programmatic API, route and pathname can be different. +> "route" is your actual page's path while "pathname" is the path of the url mapped to it. +> +> Likewise, url and path is the same usually. +> But when used with programmatic API, "url" is the route with the query string. +> "pathname" is the path of the url mapped to it. + ### Prefetching Pages -Next.js exposes a module that configures a `ServiceWorker` automatically to prefetch pages: `next/prefetch`. +Next.js exposes a module that configures a `ServiceWorker` automatically to prefetch pages: `next/prefetch`. -Since Next.js server-renders your pages, this allows all the future interaction paths of your app to be instant. Effectively Next.js gives you the great initial download performance of a _website_, with the ahead-of-time download capabilities of an _app_. [Read more](https://zeit.co/blog/next#anticipation-is-the-key-to-performance). +Since Next.js server-renders your pages, this allows all the future interaction paths of your app to be instant. Effectively Next.js gives you the great initial download performance of a _website_, with the ahead-of-time download capabilities of an _app_. [Read more](https://zeit.co/blog/next#anticipation-is-the-key-to-performance). #### Link prefetching @@ -251,7 +294,7 @@ export default class Error extends React.Component { ### Custom configuration -For custom advanced behavior of Next.js, you can create a `next.config.js` in the root of your project directory (next to `pages/` and `package.json`). +For custom advanced behavior of Next.js, you can create a `next.config.js` in the root of your project directory (next to `pages/` and `package.json`). Note: `next.config.js` is a regular Node.js module, not a JSON file. It gets used by the Next server and build phases, and not included in the browser build. @@ -264,7 +307,7 @@ module.exports = { ### Customizing webpack config -In order to extend our usage of `webpack`, you can define a function that extends its config. +In order to extend our usage of `webpack`, you can define a function that extends its config. The following example shows how you can use [`react-svg-loader`](https://github.com/boopathi/react-svg-loader) to easily import any `.svg` file as a React component, without modification. diff --git a/client/next.js b/client/next.js index 0c2b7604..c44e0c29 100644 --- a/client/next.js +++ b/client/next.js @@ -1,9 +1,8 @@ import { createElement } from 'react' import { render } from 'react-dom' import HeadManager from './head-manager' -import domready from 'domready' import { rehydrate } from '../lib/css' -import Router from '../lib/router' +import { createRouter } from '../lib/router' import App from '../lib/app' import evalScript from '../lib/eval-script' @@ -19,25 +18,18 @@ const { } } = window -domready(() => { - const Component = evalScript(component).default - const ErrorComponent = evalScript(errorComponent).default +const Component = evalScript(component).default +const ErrorComponent = evalScript(errorComponent).default - const router = new Router(pathname, query, { - Component, - ErrorComponent, - ctx: { err } - }) - - // This it to support error handling in the dev time with hot code reload. - if (window.next) { - window.next.router = router - } - - const headManager = new HeadManager() - const container = document.getElementById('__next') - const appProps = { Component, props, router, headManager } - - if (ids) rehydrate(ids) - render(createElement(App, appProps), container) +export const router = createRouter(pathname, query, { + Component, + ErrorComponent, + ctx: { err } }) + +const headManager = new HeadManager() +const container = document.getElementById('__next') +const appProps = { Component, props, router, headManager } + +if (ids) rehydrate(ids) +render(createElement(App, appProps), container) diff --git a/client/webpack-hot-middleware-client.js b/client/webpack-hot-middleware-client.js index 079089e3..8e463305 100644 --- a/client/webpack-hot-middleware-client.js +++ b/client/webpack-hot-middleware-client.js @@ -1,27 +1,27 @@ -/* global next */ import webpackHotMiddlewareClient from 'webpack-hot-middleware/client?overlay=false&reload=true' +import Router from '../lib/router' const handlers = { reload (route) { if (route === '/_error') { - for (const r of Object.keys(next.router.components)) { - const { Component } = next.router.components[r] + for (const r of Object.keys(Router.components)) { + const { Component } = Router.components[r] if (Component.__route === '/_error-debug') { // reload all '/_error-debug' // which are expected to be errors of '/_error' routes - next.router.reload(r) + Router.reload(r) } } return } - next.router.reload(route) + Router.reload(route) }, change (route) { - const { Component } = next.router.components[route] || {} + const { Component } = Router.components[route] || {} if (Component && Component.__route === '/_error-debug') { // reload to recover from runtime errors - next.router.reload(route) + Router.reload(route) } }, hardReload () { diff --git a/examples/using-router/README.md b/examples/using-router/README.md new file mode 100644 index 00000000..0a0c2733 --- /dev/null +++ b/examples/using-router/README.md @@ -0,0 +1,13 @@ +# Example app utilizing next/router for routing + +This example features: + +* An app linking pages using `next/router` instead of `` component. +* Access the pathname using `next/router` and render it in a component + +## How to run it + +```sh +npm install +npm run dev +``` diff --git a/examples/using-router/components/Header.js b/examples/using-router/components/Header.js new file mode 100644 index 00000000..a07ab14b --- /dev/null +++ b/examples/using-router/components/Header.js @@ -0,0 +1,31 @@ +import React from 'react' +import Router from 'next/router' + +const styles = { + a: { + marginRight: 10 + } +} + +const Link = ({ children, href }) => ( + { + e.preventDefault() + Router.push(href) + }} + > + { children } + +) + +export default () => ( +This is the about page.
+HOME PAGE is here!
+