javascriptvue.jsvuejs3nuxt.jsnuxt3.js

Transition Issue with Layout in Nuxt 3


I am facing an issue with route transitions when conditionally disabling layouts on specific pages. I have a <NuxtPage /> in app.vue wrapped in default <NuxtLayout />, which contains a header and a footer. I don't need this layout on the 'About' page, so what I tried to do disable it there - definePageMeta({ layout: false });. The problem is that when if I use a transition and disable a layout on the 'About' page, now when I visit this page, the transition disappears. Also, I tried adding a layoutTransition in nuxt.config.ts, but I couldn't make it work having <NuxtLayout /> in app.vue.

The only solution I've found it to remove <NuxtLayout /> from app.vue and add it manually to each page, then create an emptyLayout with an empty header and footer and add it to the 'About' page. However, I feel like this approach isn't proper, so it's either I'm doing something wrong or there's currently no better way to control the transition and layouts at the same time.

The question is: how can I maintain the transition effects when disabling the layout on certain pages without having to manually handle layouts for each page?

Any solution would be greatly appreciated!

app.vue:

<template>
  <div>
    <NuxtLayout>
      <NuxtPage />
    </NuxtLayout>
  </div>
</template>

about.vue:

<template>
  <div>
      <div>...</div>
  </div>
</template>

<script setup>
definePageMeta({
  layout: false,
});
</script>

transition in nuxt.config.ts:

app: {
   pageTransition: { name: "page", mode: "out-in" }
}

Solution

  • I had this problem before, disabling the layout will disable its transition. The best way to handle this would be creating an empty layout (eg. blank.vue) that just displays the <slot /> with no other elements.

    This way you can add the same animation you use for the pages, for the layout too, and this will keep the transition between layouts.

    Here is a basic demo:

    // app.vue
    
    <template>
      <NuxtLayout>
        <NuxtPage />
      </NuxtLayout>
    </template>
    
    <style>
      .page-enter-active,
      .page-leave-active,
      .layout-enter-active,
      .layout-leave-active {
        transition: opacity 300ms ease-in-out;
      }
    
      .page-enter-from,
      .page-leave-to,
      .layout-enter-from,
      .layout-leave-to {
        opacity: 0;
      }
    </style>
    
    // default.vue - layout
    
    <template>
      <div>
        <!-- other content -->
        <slot />
        <!-- other content -->
      </div>
    </template>
    
    // blank.vue - layout
    
    <template>
      <div>
        <slot />
      </div>
    </template>
    
    // about.vue - page
    
    <template>
      <div>...</div>
    </template>
    
    <script lang="ts" setup>
      definePageMeta({
        layout: "blank"
      });
    </script>
    
    // nuxt.config.ts
    
    export default defineNuxtConfig({
      app: {
        pageTransition: {
          name: "page",
          mode: "out-in"
        },
        layoutTransition: {
          name: "layout",
          mode: "out-in"
        }
        // keeping the name different just for good practice
      }
    });