nuxt
nuxt copied to clipboard
Layout doesn't switch
Environment
- Operating System:
Darwin - Node Version:
v16.11.0 - Nuxt Version:
3.1.2 - Nitro Version:
2.1.1 - Package Manager:
[email protected] - Builder:
vite - User Config:
extends,runtimeConfig,experimental,app,modules,imports,vite,typescript,css,postcss,pwa,content,piniaPersistedstate,colorMode,plausible,linkChecker - Runtime Modules:
@kevinmarrec/[email protected],@nuxt/[email protected],@nuxtjs/[email protected],@nuxtjs/[email protected],@pinia/[email protected],@pinia-plugin-persistedstate/[email protected],@vueuse/[email protected],[email protected],[email protected],[email protected] - Build Modules:
-
Reproduction
It is hard to reproduce, because it only happens in production and within the GitHub OAuth flow. You can see the problem here https://repo-tracker.com when you click on "Sign in" (see gif + description).

Describe the bug
The landing page and the /track page (redirect after login) have different layouts, 'page' and 'pwa' respectively. When you click on sign in this composable is called:
export const useGithubLogin = () => {
if (process.client) {
const config = useRuntimeConfig()
window.location.replace(`https://github.com/login/oauth/authorize?client_id=${config.public.githubClientId}`)
}
}
redirecting to GitHub and then back (I also tried navigateTo(...), window.open(...), and window.location.href, all the same result).
The problem is that the layout gets stuck on 'page' and doesn't switch to 'pwa'. That's why the content fills the full page and doesn't have a max-width as it should. Once you reload everything is fine. I see a similar effect sometimes within the app randomly but cannot reproduce.
Here is the layout setup:
<!-- app.vue -->
<template>
<div>
<NuxtLayout>
<SeoKit />
<NuxtPage />
</NuxtLayout>
</div>
</template>
<!-- layouts/page.vue -->
<template>
<div class="wrapper">
<nav class="sticky">
<!-- Header code -->
</nav>
<main>
<div class="flex-1">
<slot />
</div>
</main>
<AppFooter />
</div>
</template>
<!-- layouts/pwa.vue -->
<template>
<div class="wrapper">
<nav class="sticky max-h-14">
<!-- Header code -->
</nav>
<main>
<div class="page-container">
<div class="content-container no-scrollbar">
<div class="content">
<slot />
</div>
</div>
</div>
</main>
</div>
</template>
The pages look both exactly the same, with the only difference being the layout name:
<!-- index.vue + track/index.vue -->
<template>
<div>
<NuxtLayout name="page / pwa">
<template #nav-items>
<!-- Items that get slotted into the header -->
</template>
<!-- page -->
</NuxtLayout>
</div>
</div>
Both have
definePageMeta({
layout: false,
})
This doesn't happen with the dev server, I only see it in production.
Super strange!
Additional context
No response
Logs
No response
PS: I would also appreciate some feedback if the layouts are set up correctly with app.vue and page.vue / pwa.vue. It is not really clear to me from the docs how that should look like.
Hi there! Where do you have this syntax from?
<template>
<div>
<NuxtLayout name="page / pwa">
<template #nav-items>
<!-- Items that get slotted into the header -->
</template>
<!-- page -->
</NuxtLayout>
</div>
</div>
Update: It's from the docs here https://nuxt.com/docs/guide/directory-structure/layouts#overriding-a-layout-on-a-per-page-basis
~~Never saw it and I think that's the issue. NuxtLayout is only meant to be used once (at least that's what I read from the docs), in the app.vue.~~ What you are trying to archive is having a layout component with a slot, called from a page. This is likely done with a components/MyLayout.vue.
Your pages then should look like this:
<!-- index.vue + track/index.vue -->
<template>
<div>
<MyLayout>
<template #nav-items>
<!-- Items that get slotted into the header -->
</template>
<!-- page -->
</MyLayout>
</div>
</div>
<script setup>
definePageMeta({
layout: 'pwa',
})
</script>
The definePageMeta is actually important to set.
Thanks for taking a look @madebyfabian!
The syntax is a mix of experimentation and previous feedback from an issue about using template slots in layouts from @danielroe. Unfortunately, the docs on this topic are still quite limited and don't cover that use case, only the most basic one.
I'm not sure if I understand your solution, but it is basically not using layouts at all and using a component instead? That way you lose all the benefits of layouts though and you have to use it on every page...
What I was trying to achieve with my layout is the following:
By default, use the layout in app.vue and choose which one with the layout property in definePageMeta (no <NuxtLayout /> component needed in this case), potentially even using a default layout at some point. Then, when you want to use the layout slot on a page you set layout: false, disabling the layout in app.vue and using the <NuxtLayout /> component instead with a name (that is documented in the Layout docs).
But that doesn't work properly, because it creates a flicker when navigating between pages that use the app.vue layout and pages that use their own layout component. So I ended up adding the <NuxtLayout /> component on every page. And on the dev server the setup described in the issue works perfectly fine.
I now realized that in this scenario I don't need the layout in app.vue at all and I removed it. Now I have:
<template>
<div id="app">
<SeoKit />
<NuxtPage />
</div>
</template>
and
<template>
<div>
<NuxtLayout name="page / pwa">
<template #nav-items>
<!-- Items that get slotted into the header -->
</template>
<!-- page -->
</NuxtLayout>
</div>
</div>
on all pages, but the problem persists. You can still see it on the live page (repo-tracker.com) when you click "sign in" (see Chrome inspector). The /track page is first rendered with the 'page' layout, even though it has the 'pwa' layout. After page reload it is correct. The layout just doesn't update correctly. And this is only happening in production, on the dev server it works fine.
@toniengelhardt I understand your point. Though I discovered several issues with layouts, especially with a custom error.vue, which made me completely disable/not use them and yes, use a custom component on every page. But for me, almost every page has something unique that I want to pass into the Layout, so if I would now use <NuxtLayout /> with a slot or <MyLayout> with slots + props, isn't really much of a difference anymore – so with your own Layout you get more control and less bugs, at least for now.
@madebyfabian I get your point, but in the end layouts should work right? And I think they actually do, it just seems to be an issue with the build.
@toniengelhardt Totally! I was just trying to help resolve this particular issue for you right now. I am just not sure if layouts are intended too work like you intend do use them. I mean for me it makes sense, but maybe @danielroe has more insights on why the layout switch here does not work.
🙏🏽
If it's a bigger issue beyond a simple bug fix I might actually get rid of the layouts altogether for this particular app. But I think for a lot of apps layouts make a lot of sense and the use case in the issue is probably one of the most common ones.
passing external props and setting to true solved the problem for me. see code below:
navigateTo('/dashboard', { external: true })
Don't forget to use with return or await
I hope this helps someone.
@toniengelhardt Didn't we resolve this in your site? (maybe a samesite cookie issue?)
@toniengelhardt Didn't we resolve this in your site? (maybe a samesite cookie issue?)
Hey @danielroe,
we resolved it in your livestream for webapicheck.com. I originally thought that it was the same issue, but I still have problems with repo-tracker.com that I can't figure out. I "fixed" it somewhat by setting the width of the container, but the problem is still there and sometimes the layout breaks completely. You can see it when you log in with GitHub, the layout shifts around after loading, something is off there and it only happens in production. I already excluded the prerendering for the relevant pages (that was the issue for the other app). It's probably an issue with my code, but it doesn't throw any errors and works in dev mode.
Not sure if it is related, but for journalisticapp.com I also get an error on screen during the login (production only and this one is SSR off):
Might be related to the redirects somehow?
If you want to take a look I can give you access to the repo?
@toniengelhardt Are you still encountering this? I think this might have been resolved in a raft of suspense fixes we merged previously.
If you're encountering a problem, let me know with a reproduction and I'll happily reopen.
@danielroe thanks for checking. I'll upgrade the app to Nuxt 3.8.0 in the next days and see if that resolves it 🙏🏽
@danielroe I'm still having this issue in 3.8.2 when redirecting via global middleware - the only "fix" is to set external to true despite it being an internal redirect.
@MikeBman I think this is likely an issue with your implementation (see my comments above).
But if you can raise an issue with a reproduction I would be very happy to have a look.