content
content copied to clipboard
Setting layout in Front Matter
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 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.
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:
- Run
yarn create nuxt-content-docs test-layout. - Run
yarn linkto replace installedcontent-theme-docspackage with that in my local. - Set
layout: singlein the front matter ofsetup.mdfile. - Run
yarn run dev, and check whether layout is changed dynamically when I move betweenindexandsetuppages. - Run
yarn run generate,yarn run start, and check whether layout is changed dynamically when I move between the 2 pages.
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
documentanddocument.layoutto thecontextsince the latter can be got from the former.
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:
This will be possible only by using middleware for Content V2, the showcase will be visible once we open source Docus.
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.