diff --git a/examples/with-loading/README.md b/examples/with-loading/README.md
new file mode 100644
index 0000000000000..fcea460e5702d
--- /dev/null
+++ b/examples/with-loading/README.md
@@ -0,0 +1,22 @@
+# Example app with page loading indicator
+
+This example features:
+
+* An app with two pages which uses a common [Header](./components/Header.js) component for navigation links.
+* Customized version of [Link](./components/MyLink.js) which implements a loading indicator for client side navigations.
+* Uses [nprogress](https://github.com/rstacruz/nprogress) as the loading indicator.
+
+## How to run it
+
+To run it:
+
+```sh
+npm install
+npm run dev
+```
+
+To deploy it, install [now](https://zeit.co/now) and run:
+
+```sh
+now
+```
diff --git a/examples/with-loading/components/Header.js b/examples/with-loading/components/Header.js
new file mode 100644
index 0000000000000..20f037ec17a18
--- /dev/null
+++ b/examples/with-loading/components/Header.js
@@ -0,0 +1,19 @@
+import React from 'react'
+import Head from 'next/head'
+import MyLink from './MyLink'
+
+export default () => (
+
+
+ {/* Import CSS for nprogress */}
+
+
+
+
+ Home
+ |
+
+ About
+
+
+)
diff --git a/examples/with-loading/components/MyLink.js b/examples/with-loading/components/MyLink.js
new file mode 100644
index 0000000000000..78422f8542b8c
--- /dev/null
+++ b/examples/with-loading/components/MyLink.js
@@ -0,0 +1,11 @@
+import React from 'react'
+import Link from 'next/link'
+import NProgress from 'nprogress'
+
+export default (props) => (
+ NProgress.start()}
+ onComplete={() => NProgress.done()}
+ />
+)
diff --git a/examples/with-loading/package.json b/examples/with-loading/package.json
new file mode 100644
index 0000000000000..3146045703d3c
--- /dev/null
+++ b/examples/with-loading/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "with-loading",
+ "version": "0.0.0",
+ "description": "",
+ "scripts": {
+ "dev": "next"
+ },
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "nprogress": "^0.2.0",
+ "next": "*"
+ }
+}
diff --git a/examples/with-loading/pages/about.js b/examples/with-loading/pages/about.js
new file mode 100644
index 0000000000000..5fd2ba798f779
--- /dev/null
+++ b/examples/with-loading/pages/about.js
@@ -0,0 +1,20 @@
+import React, { Component } from 'react'
+import Header from '../components/Header'
+
+export default class About extends Component {
+ // Add some delay
+ static getInitialProps () {
+ return new Promise((resolve) => {
+ setTimeout(resolve, 500)
+ })
+ }
+
+ render () {
+ return (
+
+
+
This is about Next!
+
+ )
+ }
+}
diff --git a/examples/with-loading/pages/index.js b/examples/with-loading/pages/index.js
new file mode 100644
index 0000000000000..e147bfe8afa2e
--- /dev/null
+++ b/examples/with-loading/pages/index.js
@@ -0,0 +1,9 @@
+import React from 'react'
+import Header from '../components/Header'
+
+export default () => (
+
+)
diff --git a/examples/with-loading/static/nprogress.css b/examples/with-loading/static/nprogress.css
new file mode 100644
index 0000000000000..12d8abdbf98c7
--- /dev/null
+++ b/examples/with-loading/static/nprogress.css
@@ -0,0 +1,73 @@
+/* Make clicks pass-through */
+#nprogress {
+ pointer-events: none;
+}
+
+#nprogress .bar {
+ background: #29d;
+
+ position: fixed;
+ z-index: 1031;
+ top: 0;
+ left: 0;
+
+ width: 100%;
+ height: 2px;
+}
+
+/* Fancy blur effect */
+#nprogress .peg {
+ display: block;
+ position: absolute;
+ right: 0px;
+ width: 100px;
+ height: 100%;
+ box-shadow: 0 0 10px #29d, 0 0 5px #29d;
+ opacity: 1.0;
+
+ -webkit-transform: rotate(3deg) translate(0px, -4px);
+ -ms-transform: rotate(3deg) translate(0px, -4px);
+ transform: rotate(3deg) translate(0px, -4px);
+}
+
+/* Remove these to get rid of the spinner */
+#nprogress .spinner {
+ display: block;
+ position: fixed;
+ z-index: 1031;
+ top: 15px;
+ right: 15px;
+}
+
+#nprogress .spinner-icon {
+ width: 18px;
+ height: 18px;
+ box-sizing: border-box;
+
+ border: solid 2px transparent;
+ border-top-color: #29d;
+ border-left-color: #29d;
+ border-radius: 50%;
+
+ -webkit-animation: nprogress-spinner 400ms linear infinite;
+ animation: nprogress-spinner 400ms linear infinite;
+}
+
+.nprogress-custom-parent {
+ overflow: hidden;
+ position: relative;
+}
+
+.nprogress-custom-parent #nprogress .spinner,
+.nprogress-custom-parent #nprogress .bar {
+ position: absolute;
+}
+
+@-webkit-keyframes nprogress-spinner {
+ 0% { -webkit-transform: rotate(0deg); }
+ 100% { -webkit-transform: rotate(360deg); }
+}
+@keyframes nprogress-spinner {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
diff --git a/lib/link.js b/lib/link.js
index cba86bb43bd76..96c8b2d763100 100644
--- a/lib/link.js
+++ b/lib/link.js
@@ -26,15 +26,22 @@ export default class Link extends Component {
e.preventDefault()
+ // getting navigation event props.
+ const {
+ onStart = () => null,
+ onComplete = () => null,
+ onError = () => null
+ } = this.props
+
// straight up redirect
+ onStart()
this.context.router.push(null, href)
.then((success) => {
+ onComplete(success)
if (!success) return
if (scroll !== false) window.scrollTo(0, 0)
})
- .catch((err) => {
- if (this.props.onError) this.props.onError(err)
- })
+ .catch(onError)
}
render () {