Developers

Nuxt Content

Learn how to make your website editable through Studio.

learn the mdc syntax with nuxt contentBackground

What is Nuxt Content?

@nuxt/content is a Nuxt module that allows developers to integrate Markdown content into their Nuxt applications. This enables developers to focus on the website's structure and design, while allowing editors to manage content using our powerful Markdown syntax.

To build a custom application, it's essential to understand how this module works. Don't hesitate to consult the documentation for detailed information.

Nuxt Studio leverages this module to provide a seamless way to create and edit content with a live preview, all without leaving your browser.

If you're already using @nuxt/content v2, your website is already compatible with Nuxt Studio.

Demo

Let's create a demo where developers can focus on website structure and design, leaving content editing to the experts.

Minimal Starter

Nuxt Content offers a minimal starter that you can clone directly from Studio. Below is what it looks like when deployed in Studio.

Studio interface with minimal starter

Customize Everything

Not quite appealing, is it? Let's clone it from GitHub and enhance its visual appeal.

As a developer, I prioritize efficiency, and because I love Nuxt and Tailwind, I'll use Nuxt UI Pro to save time. You can, of course, use any library or CSS framework of your choice.

The first thing we should do is to improve the basic app.vue of our starter. Here's how it currently looks:

app.vue
<template>
  <div>
    <NuxtPage />
  </div>
</template>

Let's add a header, a footer, and encapsulate everything in the appropriate components.

app.vue
<template>
  <div>
    <UHeader>
      <template #logo>
        <span>
          Demo Studio
        </span>
      </template>
      <template #right>
        <UColorModeButton />
      </template>
    </UHeader>
    <UMain>
      <NuxtPage />
    </UMain>
    <UFooter>
      <template #right>
        <div class="flex items-center justify-center gap-4">
          <UButton
            icon="i-mdi-github"
            :to="`https://github.com/larbish`"
            color="gray"
            variant="ghost"
            target="_blank"
            :padded="false"
          />
          <UButton
            icon="i-mdi-twitter"
            :to="`https://twitter.com/_larbish`"
            color="gray"
            variant="ghost"
            target="_blank"
            :padded="false"
          />
        </div>
      </template>
    </UFooter>
  </div>
</template>

The starter is built with a simple catch-all page [...slug].vue. It uses the ContentDoc component, which, based on your current path, fetches the appropriate content file in your content folder (/about will fetch and display content/about.md).

pages/[...slug].vue
<template>
  <main>
    <ContentDoc />
  </main>
</template>

To make this page more appealing, we'll utilize the ContentRenderer component. To do this, we need to fetch the page ourselves using the queryContent composable. Now we have access to our page's front-matter data, and we can create a page header based on it. Finally we can add a navigation using the fetchContentNavigation composable.

pages/[...slug].vue
<template>
  <UMain>
    <UContainer>
      <UPage>
        <UPageHeader :title="page.title" :description="page.description" />
        <template #left>
          <UAside>
            <UNavigationTree :links="mapContentNavigation(navigation)" />
          </UAside>
        </template>
        <UPageBody prose>
          <ContentRenderer v-if="page.body" :value="page" />
        </UPageBody>
      </UPage>
    </UContainer>
  </UMain>
</template>

<script setup lang="ts">
const route = useRoute()
const { data: page } = await useAsyncData(route.path, () => queryContent(route.path).findOne())
if (!page.value) {
  throw createError({ statusCode: 404, statusMessage: 'Page not found' })
}

const { data: navigation } = await useAsyncData('navigation', () => fetchContentNavigation())
</script>
All custom components are auto imported by Nuxt UI Pro. Many more are available on the documentation.

Once you're satisfied with your design and structure, simply push your work, wait for the deployment, and it should be available on Studio.

Edit From Studio

It already looks much better! Now, let's edit the content directly from Studio.

MDC syntax

How about incorporating custom components directly into your page from the Markdown editor? You could create the component you want and give ability from Studio to add it and customise it on any page.

To achieve this, a developer needs to create a Vue component in the components/content folder. Here's an example with a PictureCard.vue component:

app.vue
<template>
  <UCard :ui="{ ring: `ring-2 ring-${color}-200 dark:ring-${color}-800`}">
    <template #header>
      <div class="flex justify-center text-2xl font-bold">
        <ContentSlot name="title" unwrap="p" />
      </div>
    </template>
    <slot />
    <template #footer>
      <div class="flex justify-center text-gray-500">
        <ContentSlot name="description" unwrap="p" />
      </div>
    </template>
  </UCard>
</template>

<script setup lang="ts">
defineProps({
  color: {
    type: String,
    default: null
  }
})
</script>

That's an easy one, a custom card based on UCard with a title, a description, a default slot where I can put a picture and color prop you can play with.

Once it's deployed, editors can easily work with it inside any Markdown file, thanks to the MDC syntax.

For a practical example, take a look at the implementation we've made for the UI Pro Docs starter.