nuxt icon indicating copy to clipboard operation
nuxt copied to clipboard

Layout doesn't switch

Open toniengelhardt opened this issue 2 years ago • 7 comments

Environment

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

Screen-2023-02-09-123507

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

toniengelhardt avatar Feb 08 '23 00:02 toniengelhardt

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.

toniengelhardt avatar Feb 10 '23 12:02 toniengelhardt


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.

madebyfabian avatar Feb 11 '23 05:02 madebyfabian

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 avatar Feb 11 '23 11:02 toniengelhardt

@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 avatar Feb 11 '23 12:02 madebyfabian

@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 avatar Feb 11 '23 13:02 toniengelhardt

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

madebyfabian avatar Feb 11 '23 14:02 madebyfabian

🙏🏽

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.

toniengelhardt avatar Feb 11 '23 14:02 toniengelhardt

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.

dtobi59 avatar May 10 '23 23:05 dtobi59

@toniengelhardt Didn't we resolve this in your site? (maybe a samesite cookie issue?)

danielroe avatar May 13 '23 22:05 danielroe

@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):

Screenshot_20230513-231854.png

Might be related to the redirects somehow?

If you want to take a look I can give you access to the repo?

toniengelhardt avatar May 13 '23 22:05 toniengelhardt

@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 avatar Oct 20 '23 22:10 danielroe

@danielroe thanks for checking. I'll upgrade the app to Nuxt 3.8.0 in the next days and see if that resolves it 🙏🏽

toniengelhardt avatar Oct 20 '23 22:10 toniengelhardt

@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 avatar Dec 06 '23 18:12 MikeBman

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

danielroe avatar Dec 06 '23 19:12 danielroe