Guide

Rendering Blocks

Render Gutenberg blocks as Vue components

@wpnuxt/blocks renders WordPress Gutenberg blocks as individual Vue components.

When to Use

ApproachUse Case
v-sanitize-htmlSimple HTML content, no customization needed
@wpnuxt/blocksCustom rendering per block type, lazy-load images, interactive blocks

Install

pnpm add @wpnuxt/blocks

Configure

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@wpnuxt/core', '@wpnuxt/blocks'],

  wpNuxtBlocks: {
    imageDomains: ['your-wordpress-site.com']
  }
})

WordPress Requirement

Install WPGraphQL Content Blocks to expose block data.

The module automatically checks for the WPGraphQL Content Blocks plugin at startup and warns if it's not installed.

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

pages/[...slug].vue
<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/:

components/blocks/CoreParagraph.vue
<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

  • CoreParagraph
  • CoreHeading
  • CoreImage
  • CoreButton
  • CoreButtons
  • CoreGallery
  • CoreQuote
  • CoreSpacer
  • CoreDetails

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:

pages/[...slug].vue
<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/headingCoreHeading).

Customizing a Block Type

Say you want to style core/image blocks with a caption overlay. Create a custom component:

components/blocks/CoreImage.vue
<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.

  • Custom Queries — Add custom fields to block queries
  • Performance — Optimize rendering and image loading
  • Images — Image handling strategies for WordPress content
Copyright © 2026