I had what I think is a similar issue to what you are describing, and I found this article very useful.
You do not need to insert the layouts directly on each page or view. Instead, create a Container layout:
// src/layouts/AppContainerLayout.vue
<template>
<component :is="this.$route.meta.layoutComponent">
<slot />
</component>
</template>
<script>
export default {
name: "AppContainerLayout"
}
</script>
Then, update your App.vue file to include the Container layout:
// src/App.vue
<script setup>
import AppContainerLayout from './layouts/AppContainerLayout.vue';
</script>
<template>
<AppContainerLayout>
<RouterView />
</AppContainerLayout>
</template>
Next, you can set up your router the same way you described but now you can add a call to a middleware to load the layout previously associated with the selected route:
// src/router/router.js
import { createRouter, createWebHistory } from "vue-router";
import { loadLayoutMiddleware } from "./middleware/loadLayoutMiddleware";
import HomeView from "../views/HomeView.vue";
import AnotherView from "../views/AnotherView.vue";
import NotFoundView from "../views/NotFoundView.vue";
const routes = [
{
path: "/",
name: "Home",
component: HomeView,
meta: {
layout: "AppLayoutDashboard",
},
},
{
path: "/another_view",
name: "Another",
component: AnotherView,
},
{
path: "/:pathMatch(.*)*",
name: "NotFound",
component: NotFoundView,
meta: {
layout: "AppLayoutError",
},
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
router.beforeEach(loadLayoutMiddleware);
export default router;
This is the layout middleware associated with the router that I found in the repository from the same article with a minor update to load the default layout when no layout is declared within the route:
// src/router/middleware/loadLayoutMiddleware.js
export async function loadLayoutMiddleware(route) {
try {
let layout = route.meta.layout || "DefaultLayout";
let layoutComponent = await import(`@/layouts/${layout}.vue`);
route.meta.layoutComponent = layoutComponent.default;
} catch (e) {
console.error("Error occurred in processing of layouts: ", e);
console.log("Mounted DefaultLayout");
let layout = "DefaultLayout";
let layoutComponent = await import(`@/layouts/${layout}.vue`);
route.meta.layoutComponent = layoutComponent.default;
}
}
You can take out the logs once you check that it works properly. Remember to clean your views from any references to layouts
// src/views/HomeView.vue:
<template>
<!-- VIEW CODE HERE -->
</template>
Finally, the only thing missing at this point is to create the custom layouts but you have that covered:
// src/layouts/AppLayoutDashboard.vue
<template>
<div style="border: 2px solid red">
<h1>AppLayoutDashboard.vue</h1>
<slot/>
</div>
</template>
// src/layouts/DefaultLayout.vue
<template>
<div style="border: 2px solid red">
<h1>DefaultLayout.vue</h1>
<slot/>
</div>
</template>
// src/layouts/AppLayoutError.vue
<template>
<div style="border: 2px solid red">
<h1>AppLayoutError.vue</h1>
<slot/>
</div>
</template>