트랜지션

Vue 또는 네이티브 브라우저 View Transitions로 페이지와 레이아웃 간 트랜지션을 적용하세요.
Nuxt는 Vue의 <Transition> 컴포넌트를 활용하여 페이지와 레이아웃 간 트랜지션을 적용합니다.

페이지 트랜지션

pages 전체에 자동 트랜지션을 적용하려면 페이지 트랜지션을 활성화할 수 있습니다.

nuxt.config.ts
export default defineNuxtConfig({
  app: {
    pageTransition: { name: 'page', mode: 'out-in' }
  },
})
레이아웃과 페이지를 모두 변경하는 경우, 여기서 설정한 페이지 트랜지션은 실행되지 않습니다. 대신 레이아웃 트랜지션을 설정해야 합니다.

페이지 간 트랜지션을 추가하려면, 다음 CSS를 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>

페이지 간 이동 시 다음과 같은 결과가 나타납니다:

특정 페이지에 다른 트랜지션을 설정하려면, 해당 페이지의 definePageMeta에서 pageTransition 키를 설정하세요:

<script setup lang="ts">
definePageMeta({
  pageTransition: {
    name: 'rotate'
  }
})
</script>

소개 페이지로 이동하면 3D 회전 효과가 추가됩니다:

레이아웃 트랜지션

layouts 전체에 자동 트랜지션을 적용하려면 레이아웃 트랜지션을 활성화할 수 있습니다.

nuxt.config.ts
export default defineNuxtConfig({
  app: {
    layoutTransition: { name: 'layout', mode: 'out-in' }
  },
})

페이지와 레이아웃 간 트랜지션을 추가하려면, 다음 CSS를 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>

페이지 간 이동 시 다음과 같은 결과가 나타납니다:

pageTransition과 유사하게, definePageMeta를 사용하여 페이지 컴포넌트에 커스텀 layoutTransition을 적용할 수 있습니다:

pages/about.vue
<script setup lang="ts">
definePageMeta({
  layout: 'orange',
  layoutTransition: {
    name: 'slide-in'
  }
})
</script>

글로벌 설정

이러한 기본 트랜지션 이름은 nuxt.config에서 전역적으로 커스터마이즈할 수 있습니다.

pageTransitionlayoutTransition 키 모두 JSON 직렬화 가능한 값으로 TransitionProps를 받아, name, mode 및 커스텀 CSS 트랜지션의 다른 유효한 트랜지션 속성을 전달할 수 있습니다.

nuxt.config.ts
export default defineNuxtConfig({
  app: {
    pageTransition: {
      name: 'fade',
      mode: 'out-in' // 기본값
    },
    layoutTransition: {
      name: 'slide',
      mode: 'out-in' // 기본값
    }
  }
})
name 속성을 변경하면, CSS 클래스 이름도 그에 맞게 변경해야 합니다.

글로벌 트랜지션 속성을 오버라이드하려면, definePageMeta를 사용하여 단일 Nuxt 페이지에 대한 페이지 또는 레이아웃 트랜지션을 정의하고, nuxt.config 파일에 전역적으로 정의된 모든 페이지 또는 레이아웃 트랜지션을 오버라이드할 수 있습니다.

pages/some-page.vue
<script setup lang="ts">
definePageMeta({
  pageTransition: {
    name: 'bounce',
    mode: 'out-in' // 기본값
  }
})
</script>

트랜지션 비활성화

특정 라우트에 대해 pageTransitionlayoutTransition을 비활성화할 수 있습니다:

pages/some-page.vue
<script setup lang="ts">
definePageMeta({
  pageTransition: false,
  layoutTransition: false
})
</script>

또는 nuxt.config에서 전역적으로 비활성화할 수 있습니다:

nuxt.config.ts
export default defineNuxtConfig({
  app: {
    pageTransition: false,
    layoutTransition: false
  }
})

자바스크립트 훅

고급 사용 사례를 위해, 자바스크립트 훅을 사용하여 Nuxt 페이지에 매우 동적이고 커스텀한 트랜지션을 만들 수 있습니다.

이 방법은 GSAP과 같은 자바스크립트 애니메이션 라이브러리에 완벽하게 어울립니다.

pages/some-page.vue
<script setup lang="ts">
definePageMeta({
  pageTransition: {
    name: 'custom-flip',
    mode: 'out-in',
    onBeforeEnter: (el) => {
      console.log('진입 전...')
    },
    onEnter: (el, done) => {},
    onAfterEnter: (el) => {}
  }
})
</script>
Transition 컴포넌트에서 사용할 수 있는 추가 자바스크립트 훅에 대해 더 알아보세요.

동적 트랜지션

조건부 로직을 사용하여 동적 트랜지션을 적용하려면, 인라인 middleware를 활용하여 to.meta.pageTransition에 다른 트랜지션 이름을 할당할 수 있습니다.

<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>

이제 페이지는 다음 id로 이동할 때 slide-left 트랜지션을, 이전 id로 이동할 때는 slide-right 트랜지션을 적용합니다:

NuxtPage와 트랜지션

app.vue에서 <NuxtPage />를 사용할 때, transition prop으로 트랜지션을 설정하여 전역적으로 트랜지션을 활성화할 수 있습니다.

app.vue
<template>
  <div>
    <NuxtLayout>
      <NuxtPage :transition="{
        name: 'bounce',
        mode: 'out-in'
      }" />
    </NuxtLayout>
  </div>
</template>
이 페이지 트랜지션은 개별 페이지에서 definePageMeta로 오버라이드할 수 없습니다.

View Transitions API (실험적)

Nuxt는 View Transitions API (참고: MDN)의 실험적 구현을 제공합니다. 이는 서로 관련 없는 다른 페이지의 요소 간에도 트랜지션이 가능한, 네이티브 브라우저 트랜지션을 구현하는 흥미로운 새로운 방법입니다.

https://nuxt-view-transitions.surge.sh에서 데모를 확인할 수 있으며, StackBlitz의 소스도 참고하세요.

Nuxt 통합은 활발히 개발 중이며, 설정 파일에서 experimental.viewTransition 옵션을 활성화하여 사용할 수 있습니다:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    viewTransition: true
  }
})

가능한 값은: false, true, 또는 'always'입니다.

true로 설정하면, Nuxt는 사용자의 브라우저가 prefers-reduced-motion: reduce를 감지할 경우 트랜지션을 적용하지 않습니다(권장). always로 설정하면 Nuxt는 항상 트랜지션을 적용하며, 사용자의 선호를 직접 반영해야 합니다.

기본적으로 view 트랜지션은 모든 pages에 대해 활성화되어 있지만, 다른 글로벌 기본값을 설정할 수 있습니다.

nuxt.config.ts
export default defineNuxtConfig({
  app: {
    // 전역적으로 view 트랜지션을 비활성화하고, 페이지별로 opt-in
    viewTransition: false
  },
})

페이지의 definePageMeta에서 viewTransition 키를 설정하여 기본 viewTransition 값을 오버라이드할 수 있습니다:

pages/about.vue
<script setup lang="ts">
definePageMeta({
  viewTransition: false
})
</script>
페이지별로 view 트랜지션을 오버라이드하려면, 반드시 experimental.viewTransition 옵션이 활성화되어 있어야 합니다.

위에서 설명한 pageTransitionlayoutTransition과 같은 Vue 트랜지션을 사용하여 새로운 View Transitions API와 동일한 결과를 얻고 있다면, 사용자의 브라우저가 최신 네이티브 웹 API를 지원하는 경우 Vue 트랜지션을 비활성화 하는 것이 좋습니다. 이를 위해 다음과 같이 ~/middleware/disable-vue-transitions.global.ts 파일을 생성하세요:

export default defineNuxtRouteMiddleware(to => {
  if (import.meta.server || !document.startViewTransition) { return }

  // 내장 Vue 트랜지션 비활성화
  to.meta.pageTransition = false
  to.meta.layoutTransition = false
})

알려진 이슈

  • 페이지 setup 함수 내에서 데이터 패칭을 수행하는 경우, 당분간 이 기능 사용을 재고할 수 있습니다. (설계상 View Transitions는 트랜지션이 진행되는 동안 DOM 업데이트를 완전히 정지시킵니다.) <Suspense>가 resolve되기 직전으로 View Transition을 제한하는 방안을 검토 중이지만, 그 전까지는 해당 상황에 해당된다면 이 기능의 도입을 신중히 고려하시기 바랍니다.