The elegant way to implement dynamic layout in Vue apps
In this article, we’ll talk about the fairly typical task — how to display specific layouts for different pages in a Vue app. Actually, there are many ways to implement that but we wanna share the particular approach that is elegant enough from our point of view. Basically, this stuff is targeted to juniors.
As an example, let’s imagine the Vue app consisting of few public pages and few pages of personal account area, and layouts of these sections are significantly different. E.g. there is a left sidebar at pages of the personal account area, and moreover, header and footer there look totally another way than at public pages. Besides, there are a few public pages, whose layout somehow (no matter how) differs from the majority. We also don’t know in advance what the requirements for URIs will be and how they would change and evolve. And one more requirement — you should have an ability to change the layout of any page reactively e.g. by some event or action.
That’s fine! Now, when we realize the requirements for the particular example, let’s try to push focus back and move it to a more general objective — we must be able to wrap any page with any layout that can be reactively replaced with another one (with or without changing the route — it doesn’t matter). Moreover, our solution shouldn’t impose any restrictions on app routing scheme and we should have a full flexibility in building URIs for pages.
We deliberately mentioned the need to be flexible in building URIs. Obviously, layout and page inside are just nested Vue components. And when you’re getting familiar with “VueRouter” package and its feature “nested routes”, you immediately become tempted to use it:
This approach has some disadvantages:
- Lack of flexibility in composing URIs — if you’ll have to change a page URI completely but preserving the layout, you just won’t be able to do that, since you’ll have to keep the leading URI part related to the parent route where your layout is connected as a component.
- It’s impossible to change layout reactively but keep the route.
- The strict hierarchy of routes and components ties our hands and significantly reduces the room for maneuvers.
It doesn’t look like a good architectural solution. Also, we’d seen other, more “original” approaches that also imposed some restrictions and brought difficulties. That inspired us to publish this article.
So, let’s meet “LayoutBroker”. It’s pretty simple Vue component that elegantly combines two powerful features of Vue — dynamic components and slots. The main idea is to display a layout as a dynamic component dependent on some meta param of a route, and then pass the “<router-view>” into dynamic layout via its default slot in order to render a component of a page inside a layout.
And just use in
App.vue of your application:
Each layout must provide the default slot. E.g.:
This approach doesn’t force you (but at the same time doesn’t disallow) to use nested routes and doesn’t impose any restrictions on application routing scheme as well. In this article, we don’t provide an example of reactive switching of layouts without changing the route as the most important thing here is to realize that the main objective of the LayoutBroker component is reactive layout switching and it doesn’t matter what will be used as a trigger — computed property based on current route, prop, variable in data, some state in Vuex store or something else.
Also, we shared you a bit advanced live demo. You can play around with it here.
Also we published vue-layout-broker package to NPM. Sources are here. And here is one more live demo based on this npm package.
Gists and examples at codesandbox.io updated according to the tips I’ve got in responses. Guys, thanks again!