Rendering Blocks
@wpnuxt/blocks renders WordPress Gutenberg blocks as individual Vue components.
When to Use
| Approach | Use Case |
|---|---|
v-sanitize-html | Simple HTML content, no customization needed |
@wpnuxt/blocks | Custom rendering per block type, lazy-load images, interactive blocks |
Install
pnpm add @wpnuxt/blocks
npm install @wpnuxt/blocks
Configure
export default defineNuxtConfig({
modules: ['@wpnuxt/core', '@wpnuxt/blocks'],
wpNuxtBlocks: {
imageDomains: ['your-wordpress-site.com']
}
})
WordPress Requirement
Install WPGraphQL Content Blocks to expose block data.
GraphQL Fragments
The @wpnuxt/blocks module automatically extends the Post and Page fragments from @wpnuxt/core to include editorBlocks. No manual configuration is needed.
Run nuxt prepare after adding the module to regenerate types.
Usage
<script setup lang="ts">
const route = useRoute()
const { data: page } = await usePageByUri({ uri: route.path })
</script>
<template>
<BlockRenderer v-if="page" :node="page" />
</template>
Custom Block Components
Override default rendering by creating components in components/blocks/:
<script setup lang="ts">
defineProps<{
block: {
attributes?: {
content?: string
className?: string
}
}
}>()
</script>
<template>
<p
:class="['my-custom-paragraph', block.attributes?.className]"
v-sanitize-html="block.attributes?.content"
/>
</template>
Included Components
CoreParagraphCoreHeadingCoreImageCoreButtonCoreButtonsCoreGalleryCoreQuoteCoreSpacerCoreDetails
Unsupported blocks fall back to EditorBlock which renders the saved HTML.
See Performance for tips on optimizing block rendering, including lazy-loading images and minimizing layout shift.
Directive
Use v-sanitize-html for safe HTML rendering:
<div v-sanitize-html="block.attributes?.content" />
End-to-End Example
This walkthrough shows how to fetch a page, render its blocks, and customize a specific block type.
What the GraphQL Data Looks Like
When @wpnuxt/blocks is installed, editorBlocks is automatically included in page/post queries. The data returned from GraphQL looks like this:
{
"page": {
"title": "About Us",
"editorBlocks": [
{
"name": "core/heading",
"attributes": {
"level": 2,
"content": "Our Story"
}
},
{
"name": "core/paragraph",
"attributes": {
"content": "We started building headless WordPress sites in 2023..."
}
},
{
"name": "core/image",
"attributes": {
"url": "https://your-site.com/wp-content/uploads/team.jpg",
"alt": "Our team",
"caption": "The team behind WPNuxt"
}
},
{
"name": "core/buttons",
"innerBlocks": [
{
"name": "core/button",
"attributes": {
"text": "Contact Us",
"url": "/contact"
}
}
]
}
]
}
}
Fetching and Rendering
Create a catch-all page that fetches any WordPress page by its URI and renders blocks:
<script setup lang="ts">
const route = useRoute()
const { data: page } = await usePageByUri({ uri: route.path })
if (!page.value) {
throw createError({ statusCode: 404, message: 'Page not found' })
}
useHead({ title: page.value.title })
</script>
<template>
<div>
<h1>{{ page.value?.title }}</h1>
<BlockRenderer v-if="page.value" :node="page.value" />
</div>
</template>
BlockRenderer iterates over editorBlocks and renders each block using its matching Vue component (e.g., core/heading → CoreHeading).
Customizing a Block Type
Say you want to style core/image blocks with a caption overlay. Create a custom component:
<script setup lang="ts">
defineProps<{
block: {
attributes?: {
url?: string
alt?: string
caption?: string
className?: string
}
}
}>()
</script>
<template>
<figure :class="['relative overflow-hidden rounded-lg', block.attributes?.className]">
<NuxtImg
v-if="block.attributes?.url"
:src="block.attributes.url"
:alt="block.attributes?.alt || ''"
class="w-full"
/>
<figcaption
v-if="block.attributes?.caption"
class="absolute bottom-0 left-0 right-0 bg-black/50 text-white p-3 text-sm"
v-sanitize-html="block.attributes.caption"
/>
</figure>
</template>
Place this file in components/blocks/ and it automatically overrides the default CoreImage component. No registration or configuration needed — Nuxt auto-imports it.
Related Pages
- Custom Queries — Add custom fields to block queries
- Performance — Optimize rendering and image loading
- Images — Image handling strategies for WordPress content