Nuxt leverages Vue’s <Transition> component to apply transitions between pages and layouts, providing smooth visual effects when navigating your application.
Page Transitions
Enable page transitions to apply automatic transitions for all your pages:
export default defineNuxtConfig ({
app: {
pageTransition: { name: 'page' , mode: 'out-in' },
} ,
})
If you change layouts and pages simultaneously, the page transition won’t run. Set a layout transition instead.
Add the corresponding CSS to your app.vue:
app/app.vue
app/pages/index.vue
app/pages/about.vue
< template >
< NuxtPage />
</ template >
< style >
.page-enter-active ,
.page-leave-active {
transition : all 0.4 s ;
}
.page-enter-from ,
.page-leave-to {
opacity : 0 ;
filter : blur ( 1 rem );
}
</ style >
This creates a fade and blur effect when navigating between pages.
Custom Page Transitions
Set different transitions for specific pages using definePageMeta:
pages/about.vue
app/app.vue
< script setup lang = "ts" >
definePageMeta ({
pageTransition: {
name: 'rotate' ,
},
})
</ script >
Navigating to the about page now applies a 3D rotation effect.
Layout Transitions
Enable layout transitions to apply automatic transitions for all your layouts:
export default defineNuxtConfig ({
app: {
layoutTransition: { name: 'layout' , mode: 'out-in' },
} ,
})
Add the transition styles to your app.vue:
< template >
< NuxtLayout >
< NuxtPage />
</ NuxtLayout >
</ template >
< style >
.layout-enter-active ,
.layout-leave-active {
transition : all 0.4 s ;
}
.layout-enter-from ,
.layout-leave-to {
filter : grayscale ( 1 );
}
</ style >
Custom Layout Transitions
Apply custom layout transitions using definePageMeta:
< script setup lang = "ts" >
definePageMeta ({
layout: 'orange' ,
layoutTransition: {
name: 'slide-in' ,
},
})
</ script >
Global Settings
Customize default transition names globally using nuxt.config. Both pageTransition and layoutTransition accept TransitionProps as JSON serializable values:
export default defineNuxtConfig ({
app: {
pageTransition: {
name: 'fade' ,
mode: 'out-in' ,
},
layoutTransition: {
name: 'slide' ,
mode: 'out-in' ,
},
} ,
})
If you change the name property, you must rename the CSS classes accordingly.
Override global transitions using definePageMeta for individual pages:
< script setup lang = "ts" >
definePageMeta ({
pageTransition: {
name: 'bounce' ,
mode: 'out-in' ,
},
})
</ script >
Disable Transitions
Disable transitions for specific routes:
< script setup lang = "ts" >
definePageMeta ({
pageTransition: false ,
layoutTransition: false ,
})
</ script >
Or disable globally in nuxt.config:
export default defineNuxtConfig ({
app: {
pageTransition: false ,
layoutTransition: false ,
} ,
})
JavaScript Hooks
Create highly dynamic transitions using JavaScript hooks, perfect for animation libraries like GSAP :
< script setup lang = "ts" >
definePageMeta ({
pageTransition: {
name: 'custom-flip' ,
mode: 'out-in' ,
onBeforeEnter : ( el ) => {
console . log ( 'Before enter...' )
},
onEnter : ( el , done ) => {},
onAfterEnter : ( el ) => {},
},
})
</ script >
Learn more about JavaScript hooks in the Vue Transition component documentation.
Dynamic Transitions
Apply conditional transitions using inline middleware to assign different transition names:
< script setup lang = "ts" >
definePageMeta ({
pageTransition: {
name: 'slide-right' ,
mode: 'out-in' ,
},
middleware ( to , from ) {
if ( to . meta . pageTransition && typeof to . meta . pageTransition !== 'boolean' ) {
to . meta . pageTransition . name = + to . params . id ! > + from . params . id ! ? 'slide-left' : 'slide-right'
}
},
})
</ script >
< template >
< h1 > #{{ $route . params . id }} </ h1 >
</ template >
< style >
.slide-left-enter-active ,
.slide-left-leave-active ,
.slide-right-enter-active ,
.slide-right-leave-active {
transition : all 0.2 s ;
}
.slide-left-enter-from {
opacity : 0 ;
transform : translate ( 50 px , 0 );
}
.slide-left-leave-to {
opacity : 0 ;
transform : translate ( -50 px , 0 );
}
.slide-right-enter-from {
opacity : 0 ;
transform : translate ( -50 px , 0 );
}
.slide-right-leave-to {
opacity : 0 ;
transform : translate ( 50 px , 0 );
}
</ style >
This applies slide-left when going to a higher ID and slide-right for the previous ID.
Transition with NuxtPage
Configure transitions globally using the transition prop on <NuxtPage />:
< template >
< div >
< NuxtLayout >
< NuxtPage
: transition = " {
name: 'bounce' ,
mode: 'out-in' ,
} "
/>
</ NuxtLayout >
</ div >
</ template >
This page transition cannot be overridden with definePageMeta on individual pages.
View Transitions API (Experimental)
Nuxt ships with experimental support for the native View Transitions API , enabling native browser transitions between unrelated elements on different pages.
Enable View Transitions
Enable the feature in your configuration:
export default defineNuxtConfig ({
experimental: {
viewTransition: true ,
} ,
})
Possible values:
false: Disabled
true: Enabled, respects prefers-reduced-motion
'always': Always enabled, ignores user preference
Configure Per Page
Control view transitions per page:
nuxt.config.ts
pages/about.vue
export default defineNuxtConfig ({
app: {
viewTransition: false , // Disable globally
} ,
})
View Transition Types
Use transition types to apply different CSS animations based on navigation type:
nuxt.config.ts
pages/[id].vue
export default defineNuxtConfig ({
app: {
viewTransition: {
enabled: true ,
types: [ 'slide' ],
},
} ,
})
Target these types in your CSS:
/* Slide left animation */
html :active-view-transition-type( slide-left ) {
&::view-transition-old(root) {
animation : slide-out-left 0.3 s ease-in-out ;
}
&::view-transition-new(root) {
animation : slide-in-right 0.3 s ease-in-out ;
}
}
/* Slide right animation */
html :active-view-transition-type( slide-right ) {
&::view-transition-old(root) {
animation : slide-out-right 0.3 s ease-in-out ;
}
&::view-transition-new(root) {
animation : slide-in-left 0.3 s ease-in-out ;
}
}
Function values for types only work in definePageMeta, not in nuxt.config.ts.
Disable Vue Transitions with View Transitions
If using View Transitions API, disable Vue transitions to avoid conflicts:
middleware/disable-vue-transitions.global.ts
export default defineNuxtRouteMiddleware (( to ) => {
if ( import . meta . server || ! document . startViewTransition ) {
return
}
// Disable built-in Vue transitions
to . meta . pageTransition = false
to . meta . layoutTransition = false
} )
Known Issues
If you perform data fetching within page setup functions, consider waiting to adopt this feature. View Transitions freeze DOM updates during execution. We’re working on restricting transitions to the final moments before Suspense resolves.