0
# Content Rendering
1
2
Vue component system for rendering content with MDC (Markdown Components) support, custom component mapping, and prose styling. Enables rich content experiences with Vue components embedded in Markdown.
3
4
## Capabilities
5
6
### ContentRenderer Component
7
8
Primary component for rendering parsed content with full MDC support and customization options.
9
10
```typescript { .api }
11
interface ContentRendererProps {
12
/** Content object to render (required) */
13
value: object;
14
/** Render only excerpt portion of content */
15
excerpt?: boolean;
16
/** Root HTML tag for wrapper element */
17
tag?: string;
18
/** Custom component mapping for MDC rendering */
19
components?: Record<string, Component>;
20
/** Additional data passed to rendered components */
21
data?: Record<string, unknown>;
22
/** Use prose components instead of plain HTML tags */
23
prose?: boolean;
24
/** CSS classes for root element */
25
class?: string | Record<string, boolean>;
26
/** Tags to unwrap from rendered content */
27
unwrap?: boolean | string;
28
}
29
30
interface ContentRendererSlots {
31
/** Rendered when content value is empty or null */
32
empty(): VNode[];
33
}
34
```
35
36
**Usage Examples:**
37
38
```vue
39
<template>
40
<!-- Basic content rendering -->
41
<ContentRenderer :value="article" />
42
43
<!-- Render only excerpt -->
44
<ContentRenderer :value="article" excerpt />
45
46
<!-- Custom wrapper tag -->
47
<ContentRenderer :value="article" tag="article" />
48
49
<!-- Custom components for MDC -->
50
<ContentRenderer
51
:value="article"
52
:components="{
53
'custom-alert': AlertComponent,
54
'code-block': CodeBlockComponent
55
}"
56
/>
57
58
<!-- Pass additional data to components -->
59
<ContentRenderer
60
:value="article"
61
:data="{ author: currentUser, theme: 'dark' }"
62
/>
63
64
<!-- Use prose styling -->
65
<ContentRenderer :value="article" prose class="prose-lg" />
66
67
<!-- Handle empty content -->
68
<ContentRenderer :value="article">
69
<template #empty>
70
<p>No content available</p>
71
</template>
72
</ContentRenderer>
73
74
<!-- Unwrap specific tags -->
75
<ContentRenderer :value="article" unwrap="p" />
76
</template>
77
78
<script setup>
79
import AlertComponent from '~/components/Alert.vue';
80
import CodeBlockComponent from '~/components/CodeBlock.vue';
81
82
const { data: article } = await queryCollection('articles')
83
.path(useRoute().path)
84
.first();
85
</script>
86
```
87
88
### ContentPreviewMode Component
89
90
Component for enabling live content editing and preview functionality.
91
92
```typescript { .api }
93
interface ContentPreviewModeProps {
94
/** Authentication token for preview mode (required) */
95
previewToken: string;
96
/** API endpoint URL for preview data (required) */
97
api: string;
98
/** Preview initialization callback (required) */
99
initializePreview: (preview: PreviewInstance) => void;
100
}
101
102
interface PreviewInstance {
103
/** Enable preview mode */
104
enable(): void;
105
/** Disable preview mode */
106
disable(): void;
107
/** Check if preview is active */
108
isActive(): boolean;
109
/** Refresh preview data */
110
refresh(): Promise<void>;
111
}
112
```
113
114
**Usage Examples:**
115
116
```vue
117
<template>
118
<div>
119
<!-- Content with preview mode -->
120
<ContentRenderer :value="content" />
121
122
<!-- Preview mode component -->
123
<ContentPreviewMode
124
:preview-token="previewToken"
125
:api="previewApi"
126
:initialize-preview="handlePreviewInit"
127
/>
128
</div>
129
</template>
130
131
<script setup>
132
const previewToken = useCookie('preview-token');
133
const previewApi = '/api/preview';
134
135
let previewInstance: PreviewInstance;
136
137
const handlePreviewInit = (preview: PreviewInstance) => {
138
previewInstance = preview;
139
140
// Auto-enable preview if token exists
141
if (previewToken.value) {
142
preview.enable();
143
}
144
};
145
146
// Toggle preview mode
147
const togglePreview = () => {
148
if (previewInstance.isActive()) {
149
previewInstance.disable();
150
} else {
151
previewInstance.enable();
152
}
153
};
154
</script>
155
```
156
157
## MDC Component Integration
158
159
### Custom Component Registration
160
161
```typescript
162
// nuxt.config.ts
163
export default defineNuxtConfig({
164
content: {
165
renderer: {
166
// Global component mapping
167
components: {
168
'alert': '~/components/Alert.vue',
169
'code-sandbox': '~/components/CodeSandbox.vue',
170
'video-player': '~/components/VideoPlayer.vue'
171
}
172
}
173
}
174
});
175
```
176
177
### Component Usage in Markdown
178
179
```markdown
180
<!-- In your .md files -->
181
182
# My Article
183
184
Regular markdown content here.
185
186
::alert{type="warning"}
187
This is a custom alert component with props!
188
::
189
190
::code-sandbox{id="vue-example"}
191
::
192
193
::video-player{src="/videos/demo.mp4" autoplay}
194
::
195
```
196
197
### Custom Component Definition
198
199
```vue
200
<!-- components/Alert.vue -->
201
<template>
202
<div :class="alertClasses">
203
<icon :name="iconName" />
204
<div class="alert-content">
205
<slot />
206
</div>
207
</div>
208
</template>
209
210
<script setup>
211
interface Props {
212
type?: 'info' | 'warning' | 'error' | 'success';
213
title?: string;
214
}
215
216
const props = withDefaults(defineProps<Props>(), {
217
type: 'info'
218
});
219
220
const alertClasses = computed(() => [
221
'alert',
222
`alert-${props.type}`
223
]);
224
225
const iconName = computed(() => {
226
const icons = {
227
info: 'information-circle',
228
warning: 'exclamation-triangle',
229
error: 'x-circle',
230
success: 'check-circle'
231
};
232
return icons[props.type];
233
});
234
</script>
235
```
236
237
## Tree Processing
238
239
Low-level utilities for working with content AST and tree structures.
240
241
```typescript { .api }
242
/**
243
* Compresses MDC tree to minimark format for storage/transport
244
* @param input - MDC AST root node
245
* @returns Compressed minimark tree
246
*/
247
function compressTree(input: MDCRoot): MinimarkTree;
248
249
/**
250
* Decompresses minimark tree back to MDC format
251
* @param input - Compressed tree data
252
* @returns Full MDC AST root node
253
*/
254
function decompressTree(input: Tree): MDCRoot;
255
256
/**
257
* Tree traversal utility for AST manipulation
258
* @param tree - Tree to traverse
259
* @param checker - Function to test if node should be visited
260
* @param visitor - Function to transform matching nodes
261
*/
262
function visit(
263
tree: Tree,
264
checker: (node: Node) => boolean,
265
visitor: (node: Node) => Node | undefined
266
): void;
267
```
268
269
**Usage Examples:**
270
271
```typescript
272
import { compressTree, decompressTree, visit } from '@nuxt/content/runtime';
273
274
// Compress content for storage
275
const compressed = compressTree(content.body);
276
277
// Decompress for rendering
278
const fullTree = decompressTree(compressed);
279
280
// Transform nodes in tree
281
visit(tree,
282
(node) => node.type === 'element' && node.tag === 'img',
283
(node) => {
284
// Add lazy loading to images
285
if (node.props) {
286
node.props.loading = 'lazy';
287
}
288
return node;
289
}
290
);
291
```
292
293
## Types
294
295
```typescript { .api }
296
interface MDCRoot {
297
type: 'root';
298
children: MDCNode[];
299
}
300
301
interface MDCNode {
302
type: string;
303
tag?: string;
304
props?: Record<string, unknown>;
305
children?: MDCNode[];
306
value?: string;
307
}
308
309
interface MinimarkTree {
310
type: 'root';
311
children: MinimarkNode[];
312
}
313
314
interface MinimarkNode {
315
type: string;
316
tag?: string;
317
props?: Record<string, unknown>;
318
children?: MinimarkNode[];
319
value?: string;
320
}
321
322
type Tree = MDCRoot | MinimarkTree;
323
type Node = MDCNode | MinimarkNode;
324
325
interface Component {
326
name: string;
327
props?: Record<string, unknown>;
328
slots?: Record<string, () => VNode[]>;
329
}
330
```