Skip to main content
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:
<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>
This creates a fade and blur effect when navigating between pages.

Custom Page Transitions

Set different transitions for specific pages using definePageMeta:
<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.4s;
}
.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.2s;
}
.slide-left-enter-from {
  opacity: 0;
  transform: translate(50px, 0);
}
.slide-left-leave-to {
  opacity: 0;
  transform: translate(-50px, 0);
}
.slide-right-enter-from {
  opacity: 0;
  transform: translate(-50px, 0);
}
.slide-right-leave-to {
  opacity: 0;
  transform: translate(50px, 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:
export default defineNuxtConfig({
  app: {
    viewTransition: false, // Disable globally
  },
})

View Transition Types

Use transition types to apply different CSS animations based on navigation type:
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.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;
  }
}
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:
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.