Using Layouts across multiple (non-child) pages

connecteev
0
connecteev
commented 7 months ago

What problem does this feature solve?

Is there a way to use Layouts across multiple (non-child) pages?

This is a very common use-case: Multiple pages need to look the same (3-column layout). What is the best way to do this without duplicating the HTML markup all over the place?

Here's a real example from one of my sites right now:

Feed Page:

<template>
  <div class="container tw-px-1 md:tw-px-2">
    <div class="tw-mt-4">
      <div class="tw-flex tw-flex-wrap tw-mb-4">

        <div class="tw-flex-shrink-0 tw-hidden md:tw-block tw-w-full md:tw-w-4/12 lg:tw-w-3/12 md:tw-px-4">
            <div>
                CONTENT FOR THE FEED PAGE: LEFT SIDEBAR GETS ADDED HERE
            </div>

        <div class="tw-w-full md:tw-w-8/12 lg:tw-w-6/12 md:tw-px-0">
            <div>
                CONTENT FOR THE FEED PAGE: MIDDLE COLUMN GETS ADDED HERE
            </div>
        </div>

        <div class="tw-flex-shrink-0 tw-hidden lg:tw-block md:tw-w-4/12 lg:tw-w-3/12 lg:tw-px-4">
            <div>
                CONTENT FOR THE FEED PAGE: RIGHT SIDEBAR GETS ADDED HERE
            </div>
        </div>
      </div>
    </div>
  </div>
</template>

Tags Page:

<template>
  <div class="container tw-px-1 md:tw-px-2">
    <div class="tw-mt-4">
      <div class="tw-flex tw-flex-wrap tw-mb-4">

        <div class="tw-flex-shrink-0 tw-hidden md:tw-block tw-w-full md:tw-w-4/12 lg:tw-w-3/12 md:tw-px-4">
            <div>
                CONTENT FOR THE TAGS PAGE: LEFT SIDEBAR GETS ADDED HERE
            </div>

        <div class="tw-w-full md:tw-w-8/12 lg:tw-w-6/12 md:tw-px-0">
            <div>
                CONTENT FOR THE TAGS PAGE: MIDDLE COLUMN GETS ADDED HERE
            </div>
        </div>

        <div class="tw-flex-shrink-0 tw-hidden lg:tw-block md:tw-w-4/12 lg:tw-w-3/12 lg:tw-px-4">
            <div>
                CONTENT FOR THE TAGS PAGE: RIGHT SIDEBAR GETS ADDED HERE
            </div>
        </div>
      </div>
    </div>
  </div>
</template>

As you can see in the above (simplified) example, the Feed page and Tag page share the SAME HTML structure and have a 3-column layout, but their CONTENT is completely different. I couldn't find a way to include slots within layout files. Is this possible?
This is a very common scenario where your entire site may want to use the same layout and you have to "inject" content into the right slots - how do you solve this problem? I am struggling to imagine how there isn't a solution to this problem in Nuxt.

Note:
These are NOT child-pages (or child routes) of each other, so using nuxt child pages isn't an option.
Selectively showing / hiding content with v-if is clunky and not an option.
Using component slots isn't an option because the content that should go into these slots doesn't really fit the definition of a "component".

What does the proposed changes look like?

Not sure if there's a way to use slots within layout files….but I need a solution to this.

idea
0
connecteev
0
connecteev
commented 7 months ago

I would really appreciate thoughts from the nuxt core team on this problem

0
pimlie
761
pimlie
commented 7 months ago

Are you aware you could / should use <nuxt /> in layouts? The nuxt component is a wrapper around router-view to automatically support transitions.

There are probably exceptions, but a general rule of thumb is to use <nuxt /> in layouts and use <nuxt-child /> in page components.

0
connecteev
0
connecteev
commented 7 months ago

@pimlie Very much so, and I am using <nuxt /> like this:

<template>
  <div>
    <site-header></site-header>
    <nuxt />
    <site-footer></site-footer>    
  </div>
</template>

in my base layout file. This is a very rudimentary example though. The question is how to have 2 unrelated (non-child) pages share the same structure, where each page has different content to put into it's multiple "slots". See the skeleton example I put above of Feed Page / Tag page - how can you use <nuxt /> here to solve for this?

0
pimlie
761
pimlie
commented 7 months ago

Ah right, have you seen the named-views example? You can use multiple keys for nuxt, but if you do that you need to extend the routes manually and add components for the named views (see nuxt.config.js).

Its also on csb for live trying: https://codesandbox.io/s/github/nuxt/nuxt.js/tree/dev/examples/named-views

0
connecteev
0
connecteev
commented 7 months ago

@pimlie thank you, I didn't know that named views were a thing, so this helps.
I created a slightly simplified example here for anyone else that has this need:
Demo: https://goo0w.sse.codesandbox.io/feed
Code: https://codesandbox.io/s/my-app-goo0w?fontsize=14

2 areas that I think this can be greatly improved to be cleaner. Hopefully these can both be addressed in a future version of Nuxt:

  1. With this approach you still cant inject from a page into the parent layout. You have to break off anything you want to inject into a component (components/feedTop.vue, components/feedBottom.vue for example). This would make your components grow. Other templating languages like blade, for example, allow you to inject from the base page into the parent layout it inherits from.

  2. This is the nuxt config I ended up with to extendRoutes. It would be great if in future version of Nuxt, extendRoutes was eliminated and this "just worked" without having to configure it this way. The only downside would be that the "chunkNames" and/or component names would need to be unique.

  router: {
    extendRoutes(routes, resolve) {
      let index = null;

      index = routes.findIndex(route => route.name === "feed");
      routes[index] = {
        ...routes[index],
        components: {
          default: routes[index].component,
          top: resolve(__dirname, "components/feedTop.vue"),
          bottom: resolve(__dirname, "components/feedBottom.vue")
        },
        chunkNames: {
          top: "components/feedTop",
          bottom: "components/feedBottom"
        }
      };

      index = routes.findIndex(route => route.name === "tag");
      routes[index] = {
        ...routes[index],
        components: {
          default: routes[index].component,
          top: resolve(__dirname, "components/tagTop.vue"),
          bottom: resolve(__dirname, "components/tagBottom.vue")
        },
        chunkNames: {
          top: "components/tagTop",
          bottom: "components/tagBottom"
        }
      };
    }
  }

I think it makes sense to leave this issue open to track these..but defer to you on this.

0
pimlie
761
pimlie
commented 7 months ago

Well, one of the issues is that other templating languages only have to care about the template but a SFC also contains javascript and styling.

If you want this now you could have a look at portal-vue but that doesnt support ssr. Portals should also be included by default in Vue 3, but nothing is sure about that (or when it does if it will support ssr).

0
connecteev
0
connecteev
commented 7 months ago

@pimlie interesting, look forward to this functionality in Vue 3 and future versions of Nuxt, ideally with SSR.

0
Informations
Feature RequestOpen
#c9769 - Created 7 months ago