Transitions
En esta página
::note
Nuxt leverages Vue's <Transition> component to apply transitions between pages and layouts.
::
::important
Because Nuxt uses Vue's <Transition> component, a page or layout you want to animate must have a single root element. A page or layout with multiple root elements (a fragment) cannot be animated, so the transition will not run and navigating between routes may error. Nuxt warns about this in development. Wrap the template in a single root element (for example a <div>).
::
Page Transitions#
You can enable page transitions to apply an automatic transition for all your pages.
```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { pageTransition: { name: 'page', mode: 'out-in' }, }, })
::note
If you are changing layouts as well as page, the page transition you set here will not run. Instead, you should set a [layout transition](/docs/4.x/getting-started/transitions#layout-transitions).
::
To start adding transition between your pages, add the following CSS to your [`app.vue`](/docs/4.x/directory-structure/app/app):
::code-group
```vue [app/app.vue]
<template>
<NuxtPage />
</template>
<style>
.page-enter-active,
.page-leave-active {
transition: all 0.4s;
}
.page-enter-from,
.page-leave-to {
opacity: 0;
filter: blur(1rem);
}
</style>
```vue [app/pages/index.vue]
Home page
```vue [app/pages/about.vue]
<template>
<div>
<h1>About page</h1>
<NuxtLink to="/">Home page</NuxtLink>
</div>
</template>
::
This produces the following result when navigating between pages:
To set a different transition for a page, set the pageTransition key in definePageMeta of the page:
::code-group
```vue twoslash [pages/about.vue]
```vue [app/app.vue]
<template>
<NuxtPage />
</template>
<style>
/* ... */
.rotate-enter-active,
.rotate-leave-active {
transition: all 0.4s;
}
.rotate-enter-from,
.rotate-leave-to {
opacity: 0;
transform: rotate3d(1, 1, 1, 15deg);
}
</style>
::
Moving to the about page will add the 3d rotation effect:
Layout Transitions#
You can enable layout transitions to apply an automatic transition for all your layouts.
```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { layoutTransition: { name: 'layout', mode: 'out-in' }, }, })
To start adding transition between your pages and layouts, add the following CSS to your [`app.vue`](/docs/4.x/directory-structure/app/app):
::code-group
```vue [app/app.vue]
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
<style>
.layout-enter-active,
.layout-leave-active {
transition: all 0.4s;
}
.layout-enter-from,
.layout-leave-to {
filter: grayscale(1);
}
</style>
```vue [app/layouts/default.vue]
default layout
```vue [app/layouts/orange.vue]
<template>
<div>
<pre>orange layout</pre>
<slot />
</div>
</template>
<style scoped>
div {
background-color: #eebb90;
padding: 20px;
height: 100vh;
}
</style>
```vue [app/pages/index.vue]
Home page
```vue [app/pages/about.vue]
<script setup lang="ts">
definePageMeta({
layout: 'orange',
})
</script>
<template>
<div>
<h1>About page</h1>
<NuxtLink to="/">Home page</NuxtLink>
</div>
</template>
::
This produces the following result when navigating between pages:
Similar to pageTransition, you can apply a custom layoutTransition to the page component using definePageMeta:
```vue twoslash [pages/about.vue]
## Global Settings
You can customize these default transition names globally using `nuxt.config`.
Both `pageTransition` and `layoutTransition` keys accept [`TransitionProps`](https://vuejs.org/api/built-in-components#transition) as JSON serializable values where you can pass the `name`, `mode` and other valid transition-props of the custom CSS transition.
```ts twoslash [nuxt.config.ts]
export default defineNuxtConfig({
app: {
pageTransition: {
name: 'fade',
mode: 'out-in', // default
},
layoutTransition: {
name: 'slide',
mode: 'out-in', // default
},
},
})
::warning
If you change the name property, you also have to rename the CSS classes accordingly.
::
To override the global transition property, use the definePageMeta to define page or layout transitions for a single Nuxt page and override any page or layout transitions that are defined globally in nuxt.config file.
```vue twoslash [pages/some-page.vue]
## Disable Transitions
`pageTransition` and `layoutTransition` can be disabled for a specific route:
```vue twoslash [pages/some-page.vue]
<script setup lang="ts">
definePageMeta({
pageTransition: false,
layoutTransition: false,
})
</script>
Or globally in the nuxt.config:
```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { pageTransition: false, layoutTransition: false, }, })
## JavaScript Hooks
For advanced use-cases, you can use JavaScript hooks to create highly dynamic and custom transitions for your Nuxt pages.
This way presents perfect use-cases for JavaScript animation libraries such as [GSAP](https://gsap.com).
```vue twoslash [pages/some-page.vue]
<script setup lang="ts">
definePageMeta({
pageTransition: {
name: 'custom-flip',
mode: 'out-in',
onBeforeEnter: (el) => {
console.log('Before enter...')
},
onEnter: (el, done) => {},
onAfterEnter: (el) => {},
},
})
</script>
::tip
Learn more about additional JavaScript hooks available in the Transition component.
::
Dynamic Transitions#
To apply dynamic transitions using conditional logic, you can leverage inline middleware to assign a different transition name to to.meta.pageTransition.
::code-group
```vue twoslash [pages/[id\].vue]
#{{ $route.params.id }}
```vue [app/layouts/default.vue]
<script setup lang="ts">
const route = useRoute()
const id = computed(() => Number(route.params.id || 1))
const prev = computed(() => '/' + (id.value - 1))
const next = computed(() => '/' + (id.value + 1))
</script>
<template>
<div>
<slot />
<div v-if="$route.params.id">
<NuxtLink :to="prev">⬅️</NuxtLink> |
<NuxtLink :to="next">➡️</NuxtLink>
</div>
</div>
</template>
::
The page now applies the slide-left transition when going to the next id and slide-right for the previous:
Transition with NuxtPage#
When <NuxtPage /> is used in app.vue, transitions can be configured with the transition prop to activate transitions globally.
```vue [app/app.vue]
::note
Remember, this page transition cannot be overridden with `definePageMeta` on individual pages.
::
## View Transitions API (Experimental)
Nuxt ships with an experimental implementation of the [**View Transitions API**](https://developer.chrome.com/docs/web-platform/view-transitions) (see [MDN](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API)). This is an exciting new way to implement native browser transitions which (among other things) have the ability to transition between unrelated elements on different pages.
You can check a demo [on StackBlitz](https://stackblitz.com/edit/nuxt-view-transitions).
The Nuxt integration can be enabled with the `experimental.viewTransition` option in your configuration file:
```ts twoslash [nuxt.config.ts]
export default defineNuxtConfig({
experimental: {
viewTransition: true,
},
})
The possible values are: false, true, or 'always'.
If set to true, Nuxt will not apply transitions if the user's browser matches prefers-reduced-motion: reduce (recommended). If set to always, Nuxt will always apply the transition and it is up to you to respect the user's preference.
By default, view transitions are enabled for all pages, but you can set a different global default.
```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { // Disable view transitions globally, and opt-in on a per page basis viewTransition: false, }, })
It is possible to override the default `viewTransition` value for a page by setting the `viewTransition` key in [`definePageMeta`](/docs/4.x/api/utils/define-page-meta) of the page:
```vue twoslash [pages/about.vue]
<script setup lang="ts">
definePageMeta({
viewTransition: false,
})
</script>
::warning
Overriding view transitions on a per-page basis will only have an effect if you have enabled the experimental.viewTransition option.
::
View Transition Types :badge[v4.4]{color="info" size="xs" class="align-middle"}#
View transition types allow you to apply different CSS animations depending on the type of navigation. This is useful for creating asymmetric transitions (e.g., a different animation when navigating forward vs. backward).
Types are set on the ViewTransition and can be targeted in CSS using the :active-view-transition-type() pseudo-class selector.
You can set default types globally in your nuxt.config.ts:
```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { viewTransition: { enabled: true, types: ['slide'], }, }, })
Or configure types per-page using `definePageMeta`. Per-page types support both static arrays and functions for dynamic behavior:
```vue twoslash [pages/detail.vue]
<script setup lang="ts">
definePageMeta({
viewTransition: {
enabled: true,
// Types applied to any transition involving this page
types: ['slide'],
// Types applied only when navigating TO this page
toTypes: ['slide-in'],
// Types applied only when navigating FROM this page
fromTypes: ['slide-out'],
},
})
</script>
You can also use functions for types, toTypes, and fromTypes in definePageMeta to determine types dynamically based on the route:
```vue twoslash [pages/[id].vue]
Then target these types in your CSS:
```css
/* Default crossfade */
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 0.3s;
}
/* Slide left animation */
html:active-view-transition-type(slide-left) {
&::view-transition-old(root) {
animation: slide-out-left 0.3s ease-in-out;
}
&::view-transition-new(root) {
animation: slide-in-right 0.3s ease-in-out;
}
}
/* Slide right animation */
html:active-view-transition-type(slide-right) {
&::view-transition-old(root) {
animation: slide-out-right 0.3s ease-in-out;
}
&::view-transition-new(root) {
animation: slide-in-left 0.3s ease-in-out;
}
}
::note
Function values for types, toTypes, and fromTypes only work in definePageMeta, not in nuxt.config.ts (where only static string[] is supported).
::
The page:view-transition:start hook provides access to the ViewTransition object, which includes a types property (ViewTransitionTypeSet) that can be read or modified at runtime:
```ts [plugins/view-transition.client.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('page:view-transition:start', (transition) => { // Read or modify types at runtime console.log([...transition.types]) }) })
If you are also using Vue transitions like `pageTransition` and `layoutTransition` (see above) to achieve the same result as the new View Transitions API, then you may wish to _disable_ Vue transitions if the user's browser supports the newer, native web API. You can do this by creating `~/middleware/disable-vue-transitions.global.ts` with the following contents:
```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 your page setup functions, you may wish to reconsider using this feature for the moment. (By design, View Transitions completely freeze DOM updates whilst they are taking place.) We're looking at restricting the View Transition to the final moments before
<Suspense>resolves, but in the interim you may want to consider carefully whether to adopt this feature if this describes you.