diff --git a/examples/layout-transitions/README.md b/examples/layout-transitions/README.md
new file mode 100644
index 0000000000..3bdd2a9092
--- /dev/null
+++ b/examples/layout-transitions/README.md
@@ -0,0 +1,3 @@
+# Layout transitions with Nuxt.js
+
+https://nuxtjs.org/examples/layout-transitions
diff --git a/examples/layout-transitions/assets/main.css b/examples/layout-transitions/assets/main.css
new file mode 100644
index 0000000000..dbb73ba005
--- /dev/null
+++ b/examples/layout-transitions/assets/main.css
@@ -0,0 +1,52 @@
+body {
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
+}
+
+.container {
+  text-align: center;
+  padding-top: 200px;
+  font-size: 20px;
+  transition: all .5s cubic-bezier(.55,0,.1,1);
+}
+
+.page-enter-active, .page-leave-active {
+  transition: opacity .5s
+}
+.page-enter, .page-leave-active {
+  opacity: 0
+}
+
+.layout-enter-active, .layout-leave-active {
+  transition: opacity .5s
+}
+.layout-enter, .layout-leave-active {
+  opacity: 0
+}
+
+.bounce-enter-active {
+  animation: bounce-in .8s;
+}
+.bounce-leave-active {
+  animation: bounce-out .5s;
+}
+@keyframes bounce-in {
+  0% { transform: scale(0) }
+  50% { transform: scale(1.5) }
+  100% { transform: scale(1) }
+}
+@keyframes bounce-out {
+  0% { transform: scale(1) }
+  50% { transform: scale(1.5) }
+  100% { transform: scale(0) }
+}
+
+.slide-left-enter,
+.slide-right-leave-active {
+  opacity: 0;
+  transform: translate(30px, 0);
+}
+.slide-left-leave-active,
+.slide-right-enter {
+  opacity: 0;
+  transform: translate(-30px, 0);
+}
diff --git a/examples/layout-transitions/layouts/secondary.vue b/examples/layout-transitions/layouts/secondary.vue
new file mode 100644
index 0000000000..d4a9065bd3
--- /dev/null
+++ b/examples/layout-transitions/layouts/secondary.vue
@@ -0,0 +1,11 @@
+<template>
+  <div>
+    <menu>
+      <ul>
+        <li>Option 1</li>
+        <li>Option 2</li>
+      </ul>
+    </menu>
+    <nuxt/>
+  </div>
+</template>
diff --git a/examples/layout-transitions/nuxt.config.js b/examples/layout-transitions/nuxt.config.js
new file mode 100644
index 0000000000..e1b2d64a6c
--- /dev/null
+++ b/examples/layout-transitions/nuxt.config.js
@@ -0,0 +1,10 @@
+module.exports = {
+  build: {
+    vendor: ['axios']
+  },
+  css: ['~/assets/main.css'],
+  layoutTransition: {
+    name: 'layout',
+    mode: 'out-in'
+  }
+}
diff --git a/examples/layout-transitions/package.json b/examples/layout-transitions/package.json
new file mode 100644
index 0000000000..38fd64e26c
--- /dev/null
+++ b/examples/layout-transitions/package.json
@@ -0,0 +1,12 @@
+{
+  "name": "nuxt-layout-transitions",
+  "dependencies": {
+    "axios": "^0.15.3",
+    "nuxt": "latest"
+  },
+  "scripts": {
+    "dev": "../../bin/nuxt",
+    "build": "nuxt build",
+    "start": "nuxt start"
+  }
+}
diff --git a/examples/layout-transitions/pages/about.vue b/examples/layout-transitions/pages/about.vue
new file mode 100644
index 0000000000..0ec094454f
--- /dev/null
+++ b/examples/layout-transitions/pages/about.vue
@@ -0,0 +1,13 @@
+<template>
+  <div class="container">
+    <h1>About page</h1>
+    <nuxt-link to="/">Home page</nuxt-link>
+  </div>
+</template>
+
+<script>
+export default {
+  layout: 'secondary',
+  transition: 'bounce'
+}
+</script>
diff --git a/examples/layout-transitions/pages/index.vue b/examples/layout-transitions/pages/index.vue
new file mode 100644
index 0000000000..fe5ce74005
--- /dev/null
+++ b/examples/layout-transitions/pages/index.vue
@@ -0,0 +1,7 @@
+<template>
+  <div class="container">
+    <h1>Home page</h1>
+    <p><nuxt-link to="/about">About page</nuxt-link></p>
+    <p><nuxt-link to="/users">Lists of users</nuxt-link></p>
+  </div>
+</template>
diff --git a/examples/layout-transitions/pages/users.vue b/examples/layout-transitions/pages/users.vue
new file mode 100644
index 0000000000..ce5c065ba4
--- /dev/null
+++ b/examples/layout-transitions/pages/users.vue
@@ -0,0 +1,71 @@
+<template>
+  <div class="container">
+    <nuxt-link v-if="page > 1" :to="'?page=' + (page - 1)">&lt; Prev</nuxt-link>
+    <a v-else class="disabled">&lt; Prev</a>
+    <span>{{ page }}/{{ totalPages }}</span>
+    <nuxt-link v-if="page < totalPages" :to="'?page=' + (page + 1)">Next &gt;</nuxt-link>
+    <a v-else class="disabled">Next &gt;</a>
+    <ul>
+      <li v-for="user in users">
+        <img :src="user.avatar" class="avatar" />
+        <span>{{ user.first_name }} {{ user.last_name }}</span>
+      </li>
+    </ul>
+    <p><nuxt-link to="/">Back home</nuxt-link></p>
+  </div>
+</template>
+
+<script>
+import axios from 'axios'
+
+export default {
+  transition (to, from) {
+    if (!from) return 'slide-left'
+    return +to.query.page < +from.query.page ? 'slide-right' : 'slide-left'
+  },
+  async asyncData ({ query }) {
+    const page = +query.page || 1
+    const { data } = await axios.get(`https://reqres.in/api/users?page=${page}`)
+    return {
+      page: +data.page,
+      totalPages: data.total_pages,
+      users: data.data
+    }
+  }
+}
+</script>
+
+<style scoped>
+a {
+  display: inline-block;
+  margin: 0 1em;
+  color: #34495e;
+  text-decoration: none;
+}
+a.disabled {
+  color: #ccc;
+}
+ul {
+  margin: auto;
+  padding: 0;
+  width: 100%;
+  max-width: 400px;
+  padding-top: 40px;
+}
+li {
+  list-style-type: none;
+  width: 400px;
+  border: 1px #ddd solid;
+  overflow: hidden;
+}
+li img {
+  float: left;
+  width: 100px;
+  height: 100px;
+}
+li span {
+  display: inline-block;
+  padding-top: 40px;
+  text-transform: uppercase;
+}
+</style>
diff --git a/lib/app/App.vue b/lib/app/App.vue
index 37e6d69b37..53806cd92e 100644
--- a/lib/app/App.vue
+++ b/lib/app/App.vue
@@ -1,7 +1,9 @@
 <template>
   <div id="__nuxt">
     <% if (loading) { %><nuxt-loading ref="loading"></nuxt-loading><% } %>
-    <component v-if="layout" :is="nuxt.err ? 'nuxt' : layout"></component>
+    <% if (layoutTransition) { %><transition name="<%= layoutTransition.name %>" mode="<%= layoutTransition.mode %>"><% } %>
+      <component v-if="layout" :is="nuxt.err ? 'nuxt' : layout"></component>
+    <% if (layoutTransition) { %></transition><% } %>
   </div>
 </template>
 
diff --git a/lib/app/components/nuxt-child.js b/lib/app/components/nuxt-child.js
index 39b8e3c4e7..5e17edbea1 100644
--- a/lib/app/components/nuxt-child.js
+++ b/lib/app/components/nuxt-child.js
@@ -37,6 +37,19 @@ export default {
   name: 'nuxt-child',
   functional: true,
   render (h, { parent, data }) {
+    const nuxt = parent.$root.nuxt
+    const component = parent.$route.matched[0].components.default
+
+    const layoutUid = parent._uid
+    const layoutName = component.options ? component.options.layout : null
+
+    // If we're changing layout return the stored vnode
+    if (nuxt._layoutUid === layoutUid &&
+      nuxt._layoutName !== layoutName) return nuxt._layoutVnode
+
+    nuxt._layoutUid = layoutUid
+    nuxt._layoutName = layoutName
+
     data.nuxtChild = true
     const _parent = parent
     const transitions = parent.$nuxt.nuxt.transitions
@@ -62,11 +75,14 @@ export default {
         listeners[key] = transition[key].bind(_parent)
       }
     })
-    return h('transition', {
+
+    nuxt._layoutVnode = h('transition', {
       props: transitionProps,
       on: listeners
     }, [
       h('router-view', data)
     ])
+
+    return nuxt._layoutVnode
   }
 }
diff --git a/lib/builder/builder.js b/lib/builder/builder.js
index cf1de02253..ec84c59138 100644
--- a/lib/builder/builder.js
+++ b/lib/builder/builder.js
@@ -219,6 +219,7 @@ export default class Builder extends Tapable {
       layouts: Object.assign({}, this.options.layouts),
       loading: typeof this.options.loading === 'string' ? this.relativeToBuild(this.options.srcDir, this.options.loading) : this.options.loading,
       transition: this.options.transition,
+      layoutTransition: this.options.layoutTransition,
       components: {
         ErrorPage: this.options.ErrorPage ? this.relativeToBuild(this.options.ErrorPage) : null
       }
diff --git a/lib/common/options.js b/lib/common/options.js
index 8c6f099314..7f0e5b8d32 100755
--- a/lib/common/options.js
+++ b/lib/common/options.js
@@ -24,6 +24,9 @@ Options.from = function (_options) {
   if (typeof options.transition === 'string') {
     options.transition = { name: options.transition }
   }
+  if (typeof options.layoutTransition === 'string') {
+    options.layoutTransition = { name: options.layoutTransition }
+  }
 
   // Apply defaults
   _.defaultsDeep(options, Options.defaults)
@@ -243,6 +246,10 @@ Options.defaults = {
     appearActiveClass: 'appear-active',
     appearToClass: 'appear-to'
   },
+  layoutTransition: {
+    name: 'layout',
+    mode: 'out-in'
+  },
   router: {
     mode: 'history',
     base: '/',