content icon indicating copy to clipboard operation
content copied to clipboard

Setting layout in Front Matter

Open dacmail opened this issue 5 years ago • 5 comments

It's possible to set the layout inside the front matter of markdown files? Like this:

content/blog/hello-world.md

---
layout: 'layout-1'
title: Hello World
---

content/blog/featured-article.md

---
layout: 'featured-layout'
title: Article with special layout
---

dacmail avatar Oct 06 '20 15:10 dacmail

@dacmail Hmm... I thought it'd be easy to connect the layout parameter from front matter to Nuxt's layout property. However, this doesn't work:

  ...
  async layout(context) {
    try {
      const page = context.$content(/* some fetch query */).only('layout').fetch()
      return page.layout
    } catch (err) {
      // handle somehow
      console.error(err)
      // layout will default
    }
  }
  ..

Nuxt's layout property can be a string or a function returning a string, but cannot be a Promise.

mrhubbs avatar Oct 19 '20 12:10 mrhubbs

I also dug into this issue and here are what I found.

Firstly, as @mrhubbs pointed out, layout option must be either a string or a synchronous function that returns a string. Fetching the document is asynchronous, so modifying layout option can not be an option to achieve the goal.

Another way is, as described in this issue, to modify context in middleware function which is called before layout function.

I tried this approach and it seems working.

export default {
  layout ({ store, documentLayout }) {
    return documentLayout || store.state.settings.layout || 'default'
  },
  async middleware (context) {
    const { app, params, redirect, error, $content } = context
    const path = `/${app.i18n.locale}/${params.pathMatch || 'index'}`
    const [document] = await $content({ deep: true }).where({ path }).fetch()
    if (!document) {
      return error({ statusCode: 404, message: 'Page not found' })
    }

    context.document = document
    context.documentLayout = document.layout

    if (params.pathMatch === 'index') {
      redirect(app.localePath('/'))
    }
  },
  async asyncData ({ $content, document, app }) {
    const [prev, next] = await $content(app.i18n.locale, { deep: true })
      .only(['title', 'slug', 'to'])
      .sortBy('position', 'asc')
      .surround(document.slug, { before: 1, after: 1 })
      .fetch()

    return {
      document,
      prev,
      next
    }
  },
  // ...
}

I set document as well as documentLayout just because I thought fetching document twice in middleware and asyncData would be at higher cost.

Note that I just did a quick testing in my local:

  1. Run yarn create nuxt-content-docs test-layout.
  2. Run yarn link to replace installed content-theme-docs package with that in my local.
  3. Set layout: single in the front matter of setup.md file.
  4. Run yarn run dev, and check whether layout is changed dynamically when I move between index and setup pages.
  5. Run yarn run generate, yarn run start, and check whether layout is changed dynamically when I move between the 2 pages.

nozomuikuta avatar Oct 22 '20 21:10 nozomuikuta

Oops, I missed 2 points.

  • This change must be made in content-them-docs itself, otherwise the goal cannot be achieved.
  • I didn't have to set both document and document.layout to the context since the latter can be got from the former.

nozomuikuta avatar Oct 22 '20 21:10 nozomuikuta

We are planning to support specifying a layout in the page, but actually there is a limitation with Nuxt 2 for assigning a theme dynamically, see my comment on https://github.com/nuxt/nuxt.js/issues/3510#issuecomment-736757419

This issue is also related to #669

Actually your solution is interesting @NozomuIkuta but it will make a call to the database in full static because the middleware are not mocked. Thank you really much for investigating, your are getting better and better into the Nuxt internals :grinning:

atinux avatar Dec 04 '20 16:12 atinux

This will be possible only by using middleware for Content V2, the showcase will be visible once we open source Docus.

atinux avatar May 18 '22 06:05 atinux

Using document-driven mode in nuxt/content@v2 you can set layout name in front-matter.

I'm closing this because nuxt/content@v1 will not receive any update for this.

farnabaz avatar Dec 12 '22 15:12 farnabaz