Vue Integration
This guide covers how to integrate WP Block to HTML with Vue applications for converting WordPress blocks into Vue components.
Installation
To use WP Block to HTML with Vue, install the core package and Vue integration:
# Using npm
npm install wp-block-to-html
# Using yarn
yarn add wp-block-to-html
# Using pnpm
pnpm add wp-block-to-html
Note that vue
is a peer dependency and should already be installed in your project.
Basic Usage
Here's how to convert WordPress blocks to Vue components:
<template>
<div class="wordpress-content">
<component
v-for="(component, index) in components"
:key="index"
:is="component.component"
v-bind="component.props"
/>
</div>
</template>
<script>
import { convertBlocksToVue } from 'wp-block-to-html/vue';
export default {
name: 'WordPressContent',
props: {
blocks: {
type: Array,
required: true
}
},
computed: {
components() {
// Convert blocks to Vue components
return convertBlocksToVue(this.blocks, {
cssFramework: 'tailwind'
});
}
}
}
</script>
Configuration Options
The convertBlocksToVue
function accepts the same options as the core convertBlocks
function, with additional Vue-specific options:
<script>
import { convertBlocksToVue } from 'wp-block-to-html/vue';
export default {
name: 'WordPressContent',
props: {
blocks: {
type: Array,
required: true
}
},
computed: {
components() {
return convertBlocksToVue(this.blocks, {
// Core options
cssFramework: 'tailwind',
classMap: this.customClassMap,
contentHandling: 'raw',
// Vue-specific options
components: {
// Custom component mapping
'core/image': () => import('@/components/CustomImage.vue')
},
wrapBlocks: true,
globalProps: {
// Props to pass to all components
className: 'my-custom-class'
}
});
}
}
}
</script>
Vue-Specific Options
Option | Type | Default | Description |
---|---|---|---|
components | Object | {} | Map of block names to custom Vue components |
wrapBlocks | Boolean | false | Whether to wrap each block in a div with data attributes |
globalProps | Object | {} | Props to pass to all generated components |
Using Custom Components
You can provide custom Vue components for specific blocks:
<!-- CustomImage.vue -->
<template>
<div class="image-wrapper">
<img
:src="src"
:alt="alt"
:width="width"
:height="height"
:class="className"
loading="lazy"
/>
<div class="image-caption" v-if="alt">{{ alt }}</div>
</div>
</template>
<script>
export default {
name: 'CustomImage',
props: {
src: String,
alt: String,
width: Number,
height: Number,
className: String
}
}
</script>
Then use it in your content component:
<script>
import { convertBlocksToVue } from 'wp-block-to-html/vue';
import CustomImage from '@/components/CustomImage.vue';
export default {
computed: {
components() {
return convertBlocksToVue(this.blocks, {
components: {
'core/image': CustomImage
}
});
}
}
}
</script>
Handling Events
Vue components can include event handlers:
<!-- CustomButton.vue -->
<template>
<a
:href="url"
:class="className"
@click="handleClick"
>
{{ text }}
</a>
</template>
<script>
export default {
name: 'CustomButton',
props: {
url: String,
text: String,
className: String
},
methods: {
handleClick(e) {
console.log('Button clicked:', this.text);
// Add analytics tracking or other functionality
this.$emit('button-click', { text: this.text, url: this.url });
}
}
}
</script>
Use the component in your main content component:
<template>
<div class="buttons-section">
<component
v-for="(component, index) in buttonComponents"
:key="index"
:is="component.component"
v-bind="component.props"
@button-click="trackButtonClick"
/>
</div>
</template>
<script>
import { convertBlocksToVue } from 'wp-block-to-html/vue';
import CustomButton from '@/components/CustomButton.vue';
export default {
props: {
blocks: Array
},
computed: {
buttonComponents() {
return convertBlocksToVue(this.blocks, {
components: {
'core/button': CustomButton
}
});
}
},
methods: {
trackButtonClick(data) {
// Track button click events
console.log('Button click tracked:', data);
}
}
}
</script>
Server-Side Rendering with Vue
For server-side rendering in frameworks like Nuxt.js:
<!-- pages/_slug.vue -->
<template>
<article>
<h1 v-html="post.title.rendered"></h1>
<div class="post-meta">
<time :datetime="post.date">{{ formatDate(post.date) }}</time>
</div>
<div class="post-content">
<component
v-for="(component, index) in contentComponents"
:key="index"
:is="component.component"
v-bind="component.props"
/>
</div>
</article>
</template>
<script>
import { convertBlocksToVue } from 'wp-block-to-html/vue';
import { processBlocksForSSR } from 'wp-block-to-html';
export default {
async asyncData({ params, $axios }) {
// Fetch post data from WordPress API
const response = await $axios.$get(
`https://your-wp-site.com/wp-json/wp/v2/posts?slug=${params.slug}&_embed=wp:blockdata`
);
if (response.length === 0) {
return { notFound: true };
}
const post = response[0];
let blocks = [];
if (post._embedded && post._embedded['wp:blockdata']) {
blocks = post._embedded['wp:blockdata'];
// Apply SSR optimizations
blocks = processBlocksForSSR(blocks, {
optimizationLevel: 'balanced'
});
}
return {
post,
blocks
};
},
computed: {
contentComponents() {
return convertBlocksToVue(this.blocks, {
cssFramework: 'tailwind'
});
}
},
methods: {
formatDate(date) {
return new Date(date).toLocaleDateString();
}
}
}
</script>
CSS Framework Integration
WP Block to HTML works well with various CSS frameworks in Vue:
Tailwind CSS
<template>
<div class="prose lg:prose-xl">
<component
v-for="(component, index) in components"
:key="index"
:is="component.component"
v-bind="component.props"
/>
</div>
</template>
<script>
import { convertBlocksToVue } from 'wp-block-to-html/vue';
export default {
props: {
blocks: Array
},
computed: {
components() {
return convertBlocksToVue(this.blocks, {
cssFramework: 'tailwind'
});
}
}
}
</script>
Bootstrap
<template>
<div class="container">
<div class="row">
<div class="col-md-8 offset-md-2">
<component
v-for="(component, index) in components"
:key="index"
:is="component.component"
v-bind="component.props"
/>
</div>
</div>
</div>
</template>
<script>
import { convertBlocksToVue } from 'wp-block-to-html/vue';
import 'bootstrap/dist/css/bootstrap.min.css';
export default {
props: {
blocks: Array
},
computed: {
components() {
return convertBlocksToVue(this.blocks, {
cssFramework: 'bootstrap'
});
}
}
}
</script>
Custom CSS Framework
<script>
import { convertBlocksToVue } from 'wp-block-to-html/vue';
import { getClassMap } from 'wp-block-to-html';
export default {
props: {
blocks: Array
},
computed: {
customClassMap() {
// Get the base class map for Tailwind
const tailwindClasses = getClassMap('tailwind');
// Create custom class map
return {
...tailwindClasses,
paragraph: {
base: 'my-custom-paragraph mb-4 text-gray-800',
alignLeft: 'text-left',
alignCenter: 'text-center',
alignRight: 'text-right'
},
heading: {
base: 'font-display',
level1: 'text-4xl mb-6',
level2: 'text-3xl mb-5',
level3: 'text-2xl mb-4',
level4: 'text-xl mb-3',
level5: 'text-lg mb-2',
level6: 'text-base mb-2'
}
};
},
components() {
return convertBlocksToVue(this.blocks, {
classMap: this.customClassMap
});
}
}
}
</script>
Composition API Support
WP Block to HTML works with Vue's Composition API:
<template>
<div class="wordpress-content">
<component
v-for="(component, index) in components"
:key="index"
:is="component.component"
v-bind="component.props"
/>
</div>
</template>
<script setup>
import { computed } from 'vue';
import { convertBlocksToVue } from 'wp-block-to-html/vue';
const props = defineProps({
blocks: Array
});
const components = computed(() => {
return convertBlocksToVue(props.blocks, {
cssFramework: 'tailwind'
});
});
</script>
TypeScript Support
WP Block to HTML includes TypeScript definitions for Vue integration:
<template>
<div class="wordpress-content">
<component
v-for="(component, index) in components"
:key="index"
:is="component.component"
v-bind="component.props"
/>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType, computed } from 'vue';
import { convertBlocksToVue, VueConversionOptions } from 'wp-block-to-html/vue';
import { WordPressBlock } from 'wp-block-to-html';
interface CustomImageProps {
src: string;
alt: string;
width?: number;
height?: number;
className?: string;
}
export default defineComponent({
name: 'WordPressContent',
props: {
blocks: {
type: Array as PropType<WordPressBlock[]>,
required: true
}
},
setup(props) {
const options: VueConversionOptions = {
cssFramework: 'tailwind'
};
const components = computed(() => {
return convertBlocksToVue(props.blocks, options);
});
return {
components
};
}
});
</script>
Performance Optimization
For optimal performance with Vue:
- Component Caching: Use
v-once
for static components
<template>
<div class="static-content">
<component
v-for="(component, index) in staticComponents"
:key="index"
:is="component.component"
v-bind="component.props"
v-once
/>
</div>
</template>
- Async Components: Use Vue's async component loading for heavy components
<script>
export default {
computed: {
components() {
return convertBlocksToVue(this.blocks, {
components: {
'core/gallery': () => import('@/components/Gallery.vue')
}
});
}
}
}
</script>
- Keep Components Simple: Avoid unnecessary reactivity
Vuex Integration for State Management
If you're using Vuex for state management:
<script>
import { mapState } from 'vuex';
import { convertBlocksToVue } from 'wp-block-to-html/vue';
export default {
computed: {
...mapState(['blocks']),
components() {
return convertBlocksToVue(this.blocks, {
cssFramework: 'tailwind'
});
}
}
}
</script>
Vuex store setup:
// store/index.js
import { createStore } from 'vuex';
export default createStore({
state: {
blocks: [],
post: null
},
mutations: {
SET_BLOCKS(state, blocks) {
state.blocks = blocks;
},
SET_POST(state, post) {
state.post = post;
}
},
actions: {
async fetchPost({ commit }, slug) {
try {
const response = await fetch(
`https://your-wp-site.com/wp-json/wp/v2/posts?slug=${slug}&_embed=wp:blockdata`
);
const posts = await response.json();
if (posts.length > 0) {
const post = posts[0];
commit('SET_POST', post);
if (post._embedded && post._embedded['wp:blockdata']) {
commit('SET_BLOCKS', post._embedded['wp:blockdata']);
}
}
} catch (error) {
console.error('Error fetching post:', error);
}
}
}
});
Complete Example
Here's a complete example of using WP Block to HTML with Vue:
<!-- components/WordPressPost.vue -->
<template>
<article v-if="post" class="wordpress-post">
<header>
<h1 v-html="post.title.rendered"></h1>
<div class="post-meta">
<time :datetime="post.date">{{ formatDate(post.date) }}</time>
</div>
</header>
<div class="post-content">
<component
v-for="(component, index) in contentComponents"
:key="index"
:is="component.component"
v-bind="component.props"
/>
</div>
</article>
<div v-else-if="loading" class="loading">Loading post...</div>
<div v-else class="not-found">Post not found</div>
</template>
<script>
import { ref, computed, onMounted } from 'vue';
import { convertBlocksToVue } from 'wp-block-to-html/vue';
import { processBlocksForSSR } from 'wp-block-to-html';
export default {
name: 'WordPressPost',
props: {
slug: {
type: String,
required: true
}
},
setup(props) {
const post = ref(null);
const blocks = ref([]);
const loading = ref(true);
const error = ref(null);
const contentComponents = computed(() => {
return convertBlocksToVue(blocks.value, {
cssFramework: 'tailwind',
contentHandling: 'raw'
});
});
const formatDate = (date) => {
return new Date(date).toLocaleDateString();
};
const fetchPost = async () => {
try {
loading.value = true;
const response = await fetch(
`https://your-wp-site.com/wp-json/wp/v2/posts?slug=${props.slug}&_embed=wp:blockdata`
);
if (!response.ok) {
throw new Error('Failed to fetch post');
}
const posts = await response.json();
if (posts.length > 0) {
post.value = posts[0];
if (post.value._embedded && post.value._embedded['wp:blockdata']) {
// Apply SSR optimizations to blocks
blocks.value = processBlocksForSSR(
post.value._embedded['wp:blockdata'],
{ optimizationLevel: 'balanced' }
);
}
}
} catch (err) {
error.value = err.message;
} finally {
loading.value = false;
}
};
onMounted(fetchPost);
return {
post,
loading,
error,
contentComponents,
formatDate
};
}
}
</script>
<style scoped>
.wordpress-post {
max-width: 800px;
margin: 0 auto;
}
.post-meta {
color: #666;
margin-bottom: 2rem;
}
.loading, .not-found {
text-align: center;
padding: 2rem;
}
</style>
Troubleshooting Vue Integration
Common Issues
- Components Not Rendering: Ensure blocks array has the correct format with blockName property
- Missing Block Handlers: If custom blocks aren't rendering, make sure they have appropriate handlers
- SSR Hydration Issues: For SSR, ensure Vue components produce the same output as the server-rendered HTML
Hydration Solutions
For Vue 3 and Nuxt 3, prevent hydration mismatches:
<template>
<div>
<client-only>
<component
v-for="(component, index) in components"
:key="index"
:is="component.component"
v-bind="component.props"
/>
</client-only>
<template #fallback>
<div v-html="staticHtml"></div>
</template>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { convertBlocksToVue } from 'wp-block-to-html/vue';
import { convertBlocks } from 'wp-block-to-html';
const props = defineProps({
blocks: Array
});
const components = computed(() => {
return convertBlocksToVue(props.blocks, {
cssFramework: 'tailwind'
});
});
const staticHtml = computed(() => {
return convertBlocks(props.blocks, {
cssFramework: 'tailwind'
});
});
</script>