diff --git a/examples/error-handling/app.js b/examples/error-handling/app.js
new file mode 100644
index 000000000..19942a67a
--- /dev/null
+++ b/examples/error-handling/app.js
@@ -0,0 +1,137 @@
+import Vue from 'vue'
+import VueRouter from 'vue-router'
+
+Vue.use(VueRouter)
+
+// eslint-disable-next-line no-unused-vars
+let asyncMode = true
+
+const logEl = document.querySelector('.log code')
+
+const makeError = (msg) => {
+ if (asyncMode) {
+ return new Promise((resolve, reject) => {
+ reject(new Error(msg))
+ })
+ }
+
+ throw new Error(msg)
+}
+
+// Components
+const Home = { template: '
Home
' }
+const BeforeEach = { template: 'BeforeEach
' }
+const AfterEach = { template: 'AfterEach
' }
+const BeforeEnter = {
+ template: 'BeforeRouteEnter
',
+ beforeRouteEnter () {
+ return makeError('component.BeforeRouteEnter')
+ }
+}
+const BeforeUpdate = {
+ template: 'BeforeRouteUpdate
',
+ beforeRouteUpdate () {
+ return makeError('component.BeforeRouteUpdate')
+ },
+ mounted () {
+ const currentId = +this.$route.params.id || 1
+ console.log(`change params.id from ${currentId} to ${currentId + 1}`)
+ this.$router.push({
+ name: 'component.beforeRouteUpdate',
+ params: { id: currentId + 1 }
+ })
+ }
+}
+const BeforeLeave = {
+ template: 'BeforeRouteLeave
',
+ data () {
+ return {
+ canLeave: false
+ }
+ },
+ beforeRouteLeave (to, from, next) {
+ if (from.name === 'component.beforeRouteLeave' && !this.canLeave) {
+ this.canLeave = true
+ console.log('click twice to leave route')
+ return makeError('component.beforeRouteLeave')
+ }
+
+ next()
+ }
+}
+
+const router = new VueRouter({
+ mode: 'history',
+ base: __dirname,
+ routes: [
+ { path: '/', component: Home, name: 'home' },
+ { path: '/before-enter', component: BeforeEnter, name: 'component.beforeRouteEnter' },
+ { path: '/before-update/:id', component: BeforeUpdate, name: 'component.beforeRouteUpdate' },
+ { path: '/before-leave', component: BeforeLeave, name: 'component.beforeRouteLeave' },
+ { path: '/before-each', component: BeforeEach, name: 'router.beforeEach' },
+ { path: '/after-each', component: AfterEach, name: 'router.afterEach' }
+ ]
+})
+
+router.onError((err) => {
+ const modeName = asyncMode ? 'async' : 'sync'
+ logEl.innerText = `${modeName}: ${err.message}`
+
+ console.log(
+ '%c Router.onError - ' + modeName,
+ 'background: #fff; color: #000',
+ err.message
+ )
+})
+
+router.afterEach(() => {
+ return makeError('router.afterEach')
+})
+
+// Promise same as:
+// router.beforeEach(async (to, from, next) => { throw new Error('Async error') })
+router.beforeEach((to, from, next) => {
+ if (to.name === 'router.beforeEach') {
+ return makeError('router.beforeEach')
+ }
+
+ next()
+})
+
+new Vue({
+ router,
+ data () {
+ return {
+ asyncMode
+ }
+ },
+ computed: {
+ nameMode () {
+ return this.asyncMode ? 'async' : 'sync'
+ }
+ },
+ watch: {
+ asyncMode (val) {
+ asyncMode = val
+ }
+ },
+ template: `
+
+
Error Handling
+
+ Open console - {{ nameMode }} (click)
+
+
+
+ - /home
+ - /beforeEach
+ - /afterEach
+ - /beforeRouteEnter
+ - /beforeRouteUpdate
+ - /beforeRouteLeave
+
+
+
+
+ `
+}).$mount('#app')
diff --git a/examples/error-handling/index.html b/examples/error-handling/index.html
new file mode 100644
index 000000000..93f84ad00
--- /dev/null
+++ b/examples/error-handling/index.html
@@ -0,0 +1,7 @@
+
+
+← Examples index
+
+router.onError(
)
+
+
diff --git a/examples/index.html b/examples/index.html
index 9fcb249da..78cc7de73 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -26,6 +26,7 @@ Vue Router Examples
Discrete Components
Nested Routers
Keepalive View
+ Error handling