Question:
How to change the layout depending on values in the store?


Problem

I'm playing with Nuxt3 and Pinia.

I'd like to have two layouts: one for guest users and another for authenticated users.


app.vue


<script lang="ts" setup>

</script>


<template>

  <NuxtLayout>

    <NuxtPage />

  </NuxtLayout>

</template>


layout.global.ts


import {storeToRefs} from "pinia";

import {useAuthStore} from "~/store/auth";


export default defineNuxtRouteMiddleware((to) => {

    const authStore = useAuthStore();

    const {authenticated} = storeToRefs(authStore);


    const layout = computed(() => {

        return authenticated.value ? "authenticated-layout" : "default-layout";

    });


    setPageLayout(layout)

})


Every test I made resulted in the same error:


chunk-RAKT47ZN.js:1449 [Vue warn]: Unhandled error during execution of scheduler flush. This is likely a Vue internals bug. Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/core 

  at <NuxtLayoutProvider layoutProps= {ref: RefImpl} key="authenticated-layout" name="authenticated-layout"  ... > 

  at <NuxtLayout> 

  at <App key=3 > 

  at <NuxtRoot>

warn2 @ chunk-RAKT47ZN.js:1449

logError @ chunk-RAKT47ZN.js:1623

handleError @ chunk-RAKT47ZN.js:1615

callWithErrorHandling @ chunk-RAKT47ZN.js:1567

flushJobs @ chunk-RAKT47ZN.js:1763

Promise.then (async)

queueFlush @ chunk-RAKT47ZN.js:1676

queueJob @ chunk-RAKT47ZN.js:1670

(anonymous) @ chunk-RAKT47ZN.js:7208

triggerEffect @ chunk-RAKT47ZN.js:614

triggerEffects @ chunk-RAKT47ZN.js:604

triggerRefValue @ chunk-RAKT47ZN.js:1211

(anonymous) @ chunk-RAKT47ZN.js:1371

triggerEffect @ chunk-RAKT47ZN.js:614

triggerEffects @ chunk-RAKT47ZN.js:599

triggerRefValue @ chunk-RAKT47ZN.js:1211

set value @ chunk-RAKT47ZN.js:1255

finalizeNavigation @ vue-router.js:2409

(anonymous) @ vue-router.js:2319

Promise.then (async)

pushWithRedirect @ vue-router.js:2287

push @ vue-router.js:2213

login @ login.vue:19

await in login (async)

(anonymous) @ chunk-RAKT47ZN.js:10436

callWithErrorHandling @ chunk-RAKT47ZN.js:1565

callWithAsyncErrorHandling @ chunk-RAKT47ZN.js:1573

invoker @ chunk-RAKT47ZN.js:9397

chunk-RAKT47ZN.js:9157 Uncaught (in promise) TypeError: Cannot read properties of null (reading 'parentNode')

    at parentNode (chunk-RAKT47ZN.js:9157:30)

    at ReactiveEffect.componentUpdateFn [as fn] (chunk-RAKT47ZN.js:7175:11)

    at ReactiveEffect.run (chunk-RAKT47ZN.js:423:19)

    at instance.update (chunk-RAKT47ZN.js:7212:52)

    at updateComponent (chunk-RAKT47ZN.js:7039:18)

    at processComponent (chunk-RAKT47ZN.js:6974:7)

    at patch (chunk-RAKT47ZN.js:6436:11)

    at patchSuspense (chunk-RAKT47ZN.js:2562:7)

    at Object.process (chunk-RAKT47ZN.js:2481:7)

    at patch (chunk-RAKT47ZN.js:6461:16)

parentNode @ chunk-RAKT47ZN.js:9157

componentUpdateFn @ chunk-RAKT47ZN.js:7175

run @ chunk-RAKT47ZN.js:423

instance.update @ chunk-RAKT47ZN.js:7212

updateComponent @ chunk-RAKT47ZN.js:7039

processComponent @ chunk-RAKT47ZN.js:6974

patch @ chunk-RAKT47ZN.js:6436

patchSuspense @ chunk-RAKT47ZN.js:2562

process @ chunk-RAKT47ZN.js:2481

patch @ chunk-RAKT47ZN.js:6461

componentUpdateFn @ chunk-RAKT47ZN.js:7171

run @ chunk-RAKT47ZN.js:423

instance.update @ chunk-RAKT47ZN.js:7212

callWithErrorHandling @ chunk-RAKT47ZN.js:1565

flushJobs @ chunk-RAKT47ZN.js:1763

chunk-RAKT47ZN.js:7520 Uncaught (in promise) TypeError: Cannot read properties of null (reading 'subTree')

    at move (chunk-RAKT47ZN.js:7520:28)

    at move (chunk-RAKT47ZN.js:7520:7)

    at Object.resolve (chunk-RAKT47ZN.js:2789:11)

    at chunk-RAKT47ZN.js:2912:20


I'm quite new to Vue (I mostly use React).


Did I miss something? (SSR?). Is there another way in Vue.js to do that?


Solution

I don't think you need to use computing in your case. A simple validation like this would work.


Here is an example of how you can use dynamic layout content.

~/middleware/layout.global.ts


export default defineNuxtRouteMiddleware(() => {

  const authenticated = false

  if (authenticated) {

    return setPageLayout('authenticated')

  } else {

    return setPageLayout('default')

  }

})


If you set the authenticated to true it will change the layout to authenticated.


Create the layouts


~/layouts/authenticated.vue


<template>

  <div>

    <h1>Authenticated</h1>

    <div>

      <slot />

    </div>

  </div>

</template>


~/middleware/default.vue


<template>

  <div>

    <h1>Default Layout</h1>

    <div>

      <slot />

    </div>

  </div>

</template>


Set the layout on the page


app.vue


<template>

  <div>

    <NuxtLayout>

      <NuxtPage />

    </NuxtLayout>

  </div>

</template>


~/pages/index.vue


<script lang="ts" setup>

definePageMeta({

  layout: 'authenticated'

})

</script>

<template>

  <div>

    <h1>Home page</h1>

  </div>

</template>


Tested and it works.


Suggested blog

>Create a project using Vue.js: Beginners Guide

>Authentication with Vue 3 and Firebase

>Plugins and Presets for Vuejs project

>Create a Vue.js application with CLI Services

>Create a project using Vue.js: Beginners Guide

>Create Vue.js application API


Nisha Patel

Nisha Patel

Submit
0 Answers