Introduction
Nuxt UI provides beautifully styled Prose components for your markdown content. Whether you render markdown with a library or use prose components directly, Nuxt UI applies its design system so your content automatically adapts to your application's theme.
@nuxtjs/mdc, @nuxt/content or @comark/nuxt.Otherwise, enable the
prose option in your nuxt.config.ts:export default defineNuxtConfig({
modules: ['@nuxt/ui'],
ui: {
prose: true
}
})
prose option in your vite.config.ts:import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
prose: true
})
]
})
Usage
There are multiple ways to render styled content:
- Markdown Rendering - Use ContentRenderer, MDC, Comark, or ComarkRenderer to render markdown with automatic prose styling
- Direct Vue Usage - Import and use prose components directly in your Vue templates
There are multiple ways to render styled content:
- Markdown Rendering - Use Comark or ComarkRenderer to render markdown with automatic prose styling
- Direct Vue Usage - Import and use prose components directly in your Vue templates
With ContentRenderer Nuxt only
When using @nuxt/content, render your collection pages with the <ContentRenderer> component:
<script setup lang="ts">
const route = useRoute()
const { data: page } = await useAsyncData(route.path, () => queryCollection('docs').path(route.path).first())
</script>
<template>
<ContentRenderer :value="page" />
</template>
With MDC component Nuxt only
When using @nuxtjs/mdc or @nuxt/content, use the <MDC> component to render markdown strings:
<script setup lang="ts">
const value = `# Hello World
Learn more about the **MDC** component in the [documentation](https://github.com/nuxt-content/mdc).
`
</script>
<template>
<MDC :value="value" />
</template>
@nuxtjs/mdc is being deprecated in favor of Comark. Your existing markdown files are compatible, no content changes needed.With Comark component
Comark renders markdown with built-in support for streaming. It incrementally renders tokens as they arrive, making it ideal for AI chat responses and any other dynamic markdown content.
<script setup lang="ts">
import highlight from '@comark/nuxt/plugins/highlight'
const markdown = ref('# Hello World\n\nThis is **streaming** markdown.')
</script>
<template>
<Comark :markdown="markdown" :plugins="[highlight()]" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Comark } from '@comark/vue'
import highlight from '@comark/vue/plugins/highlight'
const markdown = ref('# Hello World\n\nThis is **streaming** markdown.')
</script>
<template>
<Comark :markdown="markdown" :plugins="[highlight()]" />
</template>
highlight plugin adds syntax highlighting to code blocks. Use the :streaming prop when rendering content that arrives incrementally, such as AI responses.To support dark mode, add the following CSS to your stylesheet:html.dark .shiki span {
color: var(--shiki-dark) !important;
background-color: var(--shiki-dark-bg) !important;
font-style: var(--shiki-dark-font-style) !important;
font-weight: var(--shiki-dark-font-weight) !important;
text-decoration: var(--shiki-dark-text-decoration) !important;
}
With ComarkRenderer
Use the <ComarkRenderer> component to render a pre-parsed ComarkTree without shipping any parser or plugin code to the browser. This is useful when parsing happens on the server, in a build step, or via an API.
<script setup lang="ts">
const { data: tree } = await useFetch('/api/content')
</script>
<template>
<ComarkRenderer v-if="tree" :tree="tree" />
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ComarkRenderer } from '@comark/vue'
const tree = ref()
onMounted(async () => {
tree.value = await fetch('/api/content').then(r => r.json())
})
</script>
<template>
<ComarkRenderer v-if="tree" :tree="tree" />
</template>
With Prose components
Use prose components directly in Vue templates for maximum control:
<template>
<ProseTable>
<ProseThead>
<ProseTr>
<ProseTh>Prop</ProseTh>
<ProseTh>Default</ProseTh>
</ProseTr>
</ProseThead>
<ProseTbody>
<ProseTr>
<ProseTd>
<ProseCode>color</ProseCode>
</ProseTd>
<ProseTd>
<ProseCode>neutral</ProseCode>
</ProseTd>
</ProseTr>
</ProseTbody>
</ProseTable>
</template>
MDC Syntax
Markdown elements are automatically mapped to prose components: # Heading becomes <ProseH1>, **bold** becomes <ProseStrong>, `code` becomes <ProseCode>, and so on.
MDC Syntax goes further by letting you use Vue components directly inside markdown. It is supported by Nuxt Content, Nuxt MDC and Comark.
Regular markdown with bold and italic text.
Use pnpm add @nuxt/ui to install
Import components and use them in your templates
pnpm add @nuxt/ui
yarn add @nuxt/ui
npm install @nuxt/ui
bun add @nuxt/ui
Regular markdown with **bold** and *italic* text.
::callout{icon="i-lucide-rocket" color="primary"}
Use MDC components for rich interactions!
::
::tabs
:::tabs-item{label="Installation"}
Use pnpm add @nuxt/ui to install
:::
:::tabs-item{label="Usage"}
Import components and use them in your templates
:::
::
::code-group
```bash [pnpm]
pnpm add @nuxt/ui
```
```bash [yarn]
yarn add @nuxt/ui
```
```bash [npm]
npm install @nuxt/ui
```
```bash [bun]
bun add @nuxt/ui
```
::
Theme
Override any prose component styling in your app configuration:
export default defineAppConfig({
ui: {
prose: {
h1: {
slots: {
base: 'scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl'
}
},
p: {
base: 'leading-7 [&:not(:first-child)]:mt-6'
}
}
}
})
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
prose: {
h1: {
slots: {
base: 'scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl'
}
},
p: {
base: 'leading-7 [&:not(:first-child)]:mt-6'
}
}
}
})
]
})