Skip to content

React Integration ​

This guide covers how to integrate WP Block to HTML with React applications to efficiently convert WordPress blocks into React components.

Installation ​

To use WP Block to HTML with React, install the core package and React integration:

bash
# 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 react and react-dom are peer dependencies and should already be installed in your project.

Basic Usage ​

Here's how to convert WordPress blocks to React components:

jsx
import React from 'react';
import { convertBlocksToReact } from 'wp-block-to-html/react';

// Your WordPress blocks data
const blocks = [
  {
    blockName: 'core/paragraph',
    attrs: { 
      content: 'Hello from WordPress!',
      align: 'center'
    },
    innerBlocks: []
  }
];

function WordPressContent() {
  // Convert blocks to React components
  const components = convertBlocksToReact(blocks);
  
  return (
    <div className="wordpress-content">
      {components}
    </div>
  );
}

export default WordPressContent;

Configuration Options ​

The convertBlocksToReact function accepts the same options as the core convertBlocks function, with additional React-specific options:

jsx
import { convertBlocksToReact } from 'wp-block-to-html/react';

const components = convertBlocksToReact(blocks, {
  // Core options
  cssFramework: 'tailwind',
  classMap: customClassMap,
  contentHandling: 'raw',
  
  // React-specific options
  components: {
    // Custom component mapping
    'core/image': MyCustomImageComponent
  },
  wrapBlocks: true,
  componentProps: {
    // Props to pass to all components
    className: 'my-custom-class'
  }
});

React-Specific Options ​

OptionTypeDefaultDescription
componentsObject{}Map of block names to custom React components
wrapBlocksBooleanfalseWhether to wrap each block in a div with data attributes
componentPropsObject{}Props to pass to all generated components

Using Custom Components ​

You can provide custom React components for specific blocks:

jsx
import React from 'react';
import { convertBlocksToReact } from 'wp-block-to-html/react';

// Custom image component
function CustomImage({ src, alt, width, height, className }) {
  return (
    <div className="image-wrapper">
      <img 
        src={src} 
        alt={alt} 
        width={width} 
        height={height}
        className={className}
        loading="lazy"
      />
      <div className="image-caption">{alt}</div>
    </div>
  );
}

// Map block types to custom components
const customComponents = {
  'core/image': CustomImage
};

function WordPressContent({ blocks }) {
  const components = convertBlocksToReact(blocks, {
    components: customComponents
  });
  
  return (
    <div className="wordpress-content">
      {components}
    </div>
  );
}

Handling Events ​

React components can include event handlers:

jsx
import React from 'react';
import { convertBlocksToReact } from 'wp-block-to-html/react';

function CustomButton({ url, text, className }) {
  const handleClick = (e) => {
    console.log('Button clicked:', text);
    // Add analytics tracking or other functionality
  };
  
  return (
    <a 
      href={url} 
      className={className}
      onClick={handleClick}
    >
      {text}
    </a>
  );
}

const customComponents = {
  'core/button': CustomButton
};

function ButtonsSection({ blocks }) {
  return (
    <div className="buttons-section">
      {convertBlocksToReact(blocks, {
        components: {
          'core/button': CustomButton
        }
      })}
    </div>
  );
}

Server-Side Rendering ​

For server-side rendering (SSR) in frameworks like Next.js or Remix:

jsx
// pages/[slug].js
import { convertBlocksToReact } from 'wp-block-to-html/react';
import { processBlocksForSSR } from 'wp-block-to-html';

export async function getServerSideProps({ params }) {
  // Fetch post data from WordPress API
  const response = await fetch(`https://your-wp-site.com/wp-json/wp/v2/posts?slug=${params.slug}`);
  const posts = await response.json();
  
  if (posts.length === 0) {
    return { notFound: true };
  }
  
  const post = posts[0];
  let blocks = [];
  
  if (post._embedded && post._embedded['wp:blockdata']) {
    blocks = post._embedded['wp:blockdata'];
    
    // Apply SSR optimizations
    blocks = processBlocksForSSR(blocks, {
      optimizationLevel: 'balanced'
    });
  }
  
  return {
    props: {
      post,
      blocks
    }
  };
}

function PostPage({ post, blocks }) {
  return (
    <article>
      <h1>{post.title.rendered}</h1>
      <div className="post-content">
        {convertBlocksToReact(blocks, {
          cssFramework: 'tailwind'
        })}
      </div>
    </article>
  );
}

export default PostPage;

CSS Framework Integration ​

WP Block to HTML works well with various CSS frameworks in React:

Tailwind CSS ​

jsx
import { convertBlocksToReact } from 'wp-block-to-html/react';

function WordPressContent({ blocks }) {
  return (
    <div className="prose lg:prose-xl">
      {convertBlocksToReact(blocks, {
        cssFramework: 'tailwind'
      })}
    </div>
  );
}

Bootstrap ​

jsx
import 'bootstrap/dist/css/bootstrap.min.css';
import { convertBlocksToReact } from 'wp-block-to-html/react';

function WordPressContent({ blocks }) {
  return (
    <div className="container">
      <div className="row">
        <div className="col-md-8 offset-md-2">
          {convertBlocksToReact(blocks, {
            cssFramework: 'bootstrap'
          })}
        </div>
      </div>
    </div>
  );
}

Custom CSS Framework ​

jsx
import { convertBlocksToReact } from 'wp-block-to-html/react';
import { getClassMap } from 'wp-block-to-html';

// Get the base class map for Tailwind
const tailwindClasses = getClassMap('tailwind');

// Create custom class map
const customClassMap = {
  ...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'
  }
};

function WordPressContent({ blocks }) {
  return (
    <div className="custom-content">
      {convertBlocksToReact(blocks, {
        classMap: customClassMap
      })}
    </div>
  );
}

Handling Media ​

React components can manage media loading states:

jsx
import React, { useState } from 'react';

function OptimizedImage({ src, alt, width, height, className }) {
  const [isLoaded, setIsLoaded] = useState(false);
  
  return (
    <div className={`image-container ${!isLoaded ? 'image-loading' : ''}`}>
      {!isLoaded && <div className="image-placeholder" style={{ width, height }}></div>}
      <img 
        src={src} 
        alt={alt} 
        width={width} 
        height={height} 
        className={`${className} ${isLoaded ? 'image-loaded' : 'image-loading'}`}
        loading="lazy"
        onLoad={() => setIsLoaded(true)}
      />
    </div>
  );
}

// Use this component in your block conversion
const customComponents = {
  'core/image': OptimizedImage
};

function MediaGallery({ blocks }) {
  return (
    <div className="media-gallery">
      {convertBlocksToReact(blocks, {
        components: customComponents
      })}
    </div>
  );
}

TypeScript Support ​

WP Block to HTML includes TypeScript definitions for React integration:

tsx
import React from 'react';
import { convertBlocksToReact, ReactConversionOptions } from 'wp-block-to-html/react';
import { WordPressBlock } from 'wp-block-to-html';

interface CustomImageProps {
  src: string;
  alt: string;
  width?: number;
  height?: number;
  className?: string;
}

const CustomImage: React.FC<CustomImageProps> = ({ src, alt, width, height, className }) => {
  return (
    <img 
      src={src} 
      alt={alt} 
      width={width} 
      height={height}
      className={className}
    />
  );
};

interface WordPressContentProps {
  blocks: WordPressBlock[];
}

const WordPressContent: React.FC<WordPressContentProps> = ({ blocks }) => {
  const options: ReactConversionOptions = {
    cssFramework: 'tailwind',
    components: {
      'core/image': CustomImage
    }
  };
  
  const components = convertBlocksToReact(blocks, options);
  
  return (
    <div className="wordpress-content">
      {components}
    </div>
  );
};

export default WordPressContent;

Performance Optimization ​

For optimal performance with React:

  1. Memoization: Use React.memo for custom components to prevent unnecessary re-renders
jsx
const MemoizedImage = React.memo(function Image({ src, alt, width, height }) {
  return <img src={src} alt={alt} width={width} height={height} loading="lazy" />;
});

const customComponents = {
  'core/image': MemoizedImage
};
  1. Lazy Loading: Use dynamic imports for heavy components
jsx
import React, { Suspense, lazy } from 'react';

const LazyGallery = lazy(() => import('./components/Gallery'));

function CustomGalleryHandler({ images }) {
  return (
    <Suspense fallback={<div>Loading gallery...</div>}>
      <LazyGallery images={images} />
    </Suspense>
  );
}

const customComponents = {
  'core/gallery': CustomGalleryHandler
};
  1. Code Splitting: Split component mapping by block category
jsx
import { convertBlocksToReact } from 'wp-block-to-html/react';

// Split component mapping by category
const textComponents = {
  'core/paragraph': CustomParagraph,
  'core/heading': CustomHeading
};

const mediaComponents = {
  'core/image': CustomImage,
  'core/gallery': CustomGallery
};

function WordPressContent({ blocks }) {
  // Determine which component mappings to use based on blocks
  const hasMediaBlocks = blocks.some(block => 
    block.blockName?.startsWith('core/image') || 
    block.blockName?.startsWith('core/gallery')
  );
  
  const componentMapping = {
    ...textComponents,
    ...(hasMediaBlocks ? mediaComponents : {})
  };
  
  return (
    <div className="content">
      {convertBlocksToReact(blocks, {
        components: componentMapping
      })}
    </div>
  );
}

Complete Example ​

Here's a complete example of using WP Block to HTML with React:

jsx
import React, { useState, useEffect } from 'react';
import { convertBlocksToReact } from 'wp-block-to-html/react';
import { processBlocksForSSR } from 'wp-block-to-html';

// Custom components
const CustomHeading = ({ level, content, className }) => {
  const HeadingTag = `h${level}`;
  return <HeadingTag className={className}>{content}</HeadingTag>;
};

const CustomImage = ({ src, alt, width, height, className }) => {
  return (
    <figure className="image-figure">
      <img 
        src={src} 
        alt={alt} 
        width={width} 
        height={height}
        className={className}
        loading="lazy"
      />
      {alt && <figcaption>{alt}</figcaption>}
    </figure>
  );
};

// Component mapping
const customComponents = {
  'core/heading': CustomHeading,
  'core/image': CustomImage
};

function WordPressPostViewer({ postId }) {
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [post, setPost] = useState(null);
  const [blocks, setBlocks] = useState([]);
  
  useEffect(() => {
    async function fetchPost() {
      try {
        setLoading(true);
        const response = await fetch(
          `https://your-wp-site.com/wp-json/wp/v2/posts/${postId}?_embed=wp:blockdata`
        );
        
        if (!response.ok) {
          throw new Error('Failed to fetch post');
        }
        
        const postData = await response.json();
        setPost(postData);
        
        if (postData._embedded && postData._embedded['wp:blockdata']) {
          // Apply SSR optimizations to blocks
          const optimizedBlocks = processBlocksForSSR(
            postData._embedded['wp:blockdata'],
            { optimizationLevel: 'balanced' }
          );
          setBlocks(optimizedBlocks);
        }
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }
    
    fetchPost();
  }, [postId]);
  
  if (loading) return <div className="loading">Loading post...</div>;
  if (error) return <div className="error">Error: {error}</div>;
  if (!post) return <div className="not-found">Post not found</div>;
  
  return (
    <article className="wordpress-post">
      <header>
        <h1 className="post-title">{post.title.rendered}</h1>
        <div className="post-meta">
          <time dateTime={post.date}>{new Date(post.date).toLocaleDateString()}</time>
        </div>
      </header>
      
      <div className="post-content">
        {convertBlocksToReact(blocks, {
          cssFramework: 'tailwind',
          components: customComponents,
          wrapBlocks: true,
          componentProps: {
            className: 'post-block'
          }
        })}
      </div>
    </article>
  );
}

export default WordPressPostViewer;

Troubleshooting React Integration ​

Common Issues ​

  1. Components Not Rendering: Ensure blocks array has the correct format with blockName property
  2. Missing Block Handlers: If custom blocks aren't rendering, make sure they have appropriate handlers
  3. Hydration Errors: For SSR, ensure React components produce the same output as the server-rendered HTML
  4. Performance Issues: Use React DevTools to identify and optimize frequently re-rendering components

Hydration Solutions ​

When using SSR frameworks like Next.js, prevent hydration mismatches:

jsx
import { useEffect, useState } from 'react';
import { convertBlocksToReact } from 'wp-block-to-html/react';

function HydrationSafeContent({ blocks }) {
  const [isClient, setIsClient] = useState(false);
  
  useEffect(() => {
    setIsClient(true);
  }, []);
  
  // Render with minimal interactivity on server
  if (!isClient) {
    return (
      <div className="wordpress-content" dangerouslySetInnerHTML={{
        __html: convertBlocks(blocks, { contentHandling: 'raw' })
      }} />
    );
  }
  
  // Render full React components on client
  return (
    <div className="wordpress-content">
      {convertBlocksToReact(blocks, {
        contentHandling: 'raw',
        components: customComponents
      })}
    </div>
  );
}

Released under the MIT License.