Skip to content

Latest commit

 

History

History
238 lines (168 loc) · 8.7 KB

File metadata and controls

238 lines (168 loc) · 8.7 KB

export const metadata = { title: "Rendering Blocks", };

In headless WordPress, it's possible to take the HTML for an entire page and insert it into the DOM in your decoupled front-end app. On most sites, though, it becomes necessary to map WordPress blocks to React components in your front-end app so you can control how individual blocks are rendered. This document details how to do just that.

Note that this page only covers rendering native WordPress core blocks. Once you have that set up, if you want to go further, you can follow our documentation on how to override WordPress core blocks or work with custom blocks.

Steps

1. Basic setup

If you haven't already, follow the Basic Setup to get Faust.js set up.

2. WPGraphQL Content Blocks Plugin

1. Set up the WPGraphQL Content Blocks plugin on the WP backend. Head over to the GitHub repo and download the latest version of the wp-graphql-content-blocks plugin from the releases tab.

2. Upload the plugins .zip file to your WordPress site via the plugins page or unzip and copy the file contents into your WordPress wp-content/plugins folder manually.

3. Activate the plugin on the WordPress plugins page.

3. Test Block Queries in GraphQL IDE

Once the plugin is installed and activated, head over to the GraphQL IDE. You can find it in the WordPress toolbar.

You should be able to perform queries for the block data. There is a new field added in the Post and Page models called editorBlocks. This represents a list of available blocks for that content type:

GraphQL IDE interface showing a WPGraphQL query fetching post block data. The query requests editorBlocks fields, including name and renderedHtml, while the response displays structured JSON data containing block names and their corresponding rendered HTML content.

If you search in GraphQL IDE documentation explorer tab for the editorBlocks type you will be able to see the available block fields. The most important ones are:

  • renderedHTML: It's the HTML of the block as rendered by the render_block function.

  • name: The actual name of the block taken from its block.json spec.

  • __typename: The type of block transformed from the name field in camel-case notation.

  • apiVersion: The apiVersion of the block taken from its block.json spec.

  • innerBlocks: The inner block list of that block.

  • isDynamic: Whether the block is dynamic or not, taken from its block.json spec.

  • clientId, parentClientId: Unique identifiers for the block and the parent of the block. We will explain their usage later.

How does the plugin work?

The plugin iterates over the block.json types as registered within WordPress and creates WPGraphQL types and resolvers. As long as your blocks use the register_block_type function passing a block.json, it will be available in the system without any extra steps.

In WordPress block development, a block.json file is a configuration file that provides metadata and settings for a block. It defines the block’s name, title, icon, category, and other settings related to its appearance and behavior. For more information, see the section on block.json files in the WordPress Developer’s Handbook.

As an example, given the following block.json definition of a block:

// block.json
{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 2,
	"name": "my-plugin/notice",
	"icon": "star",
	"version": "1.0.3",
	"attributes": {
		"message": {
			"type": "string",
			"source": "html",
			"selector": ".message"
		}
	}
}

The plugin will create the following WPGraphQL type:

type MyPluginNotice {
    attributes: MyPluginNoticeAttributes;
}
type MyPluginNoticeAttributes {
    message: String;
}

When you request to resolve the message attribute for MyPluginNotice, the plugin will use a resolver that tries to extract the field by sourcing the text element using the selector. As an example, with the following HTML:

<div class="message">Hello World</div>

Since the block.json message attribute uses the .message class selector to source the text for that field, this will resolve to:

"Hello World"

Attribute Types

Currently, the plugin handles the following attribute types taken from the reference list:

  • boolean

  • number

  • integer

  • string

  • object

  • array

4. Install the Necessary Packages

Install the @faustwp/blocks NPM packages in your Next.js app.

npm install @faustwp/blocks

5.Create A Blocks Directory

In the root of your Next.js project, set up wp-blocks/index.js with this:

import { CoreBlocks } from "@faustwp/blocks";

export default {
	...CoreBlocks,
};

6.Query For Block Data

To query specific block data you need to define that data in the contentBlock as the appropriate type. Here is an example query to get all the rendered HTML from the editorBlocks field:

query GetBlocksByURI($uri: ID!) {
	post(id: $uri, idType: URI) {
		title
		editorBlocks {
			__typename
			name
			renderedHtml
			... on CoreParagraph {
				attributes {
					className
				}
			}
		}
	}
}

This WPGraphQL query is designed to retrieve specific data for a single post from WordPress, focusing on block-based content. Here’s what it does:

  1. post(id: $uri, idType: URI):

    • The query retrieves a post based on its URI. The idType: URI indicates that the post is identified by its unique slug (e.g., "example-page"), passed as a variable ($uri).
    • This ensures that the content returned corresponds to the post or page associated with that specific URI.
  2. title:

    • The title field fetches the title of the queried post. This is the only post-level information returned by the query.
  3. editorBlocks:

    • This field queries the blocks used within the post, such as paragraphs, headings, lists, etc.
    • It returns:
      • __typename: Indicates the block type (e.g., CoreParagraph, CoreHeading), allowing the frontend to distinguish between different block types.
      • name: Provides the block's name as registered in the WordPress block editor (e.g., core/paragraph).
      • renderedHtml: Contains the fully rendered HTML for the block, allowing direct rendering of the block content in the frontend.
  4. ... on CoreParagraph:

    • This is a fragment specific to CoreParagraph blocks. It queries additional attributes for paragraph blocks:
      • attributes { className }: Fetches the className attribute if it exists, allowing for custom CSS classes to be applied to paragraph blocks.

Now, we can add that query into a Next.js dynamic route file to grab a single post detail page and all its block data. For example:

import { gql, useQuery } from "@apollo/client";
import { useRouter } from "next/router";

const GET_BLOCKS_BY_URI = gql`
	query GetBlocksByURI($uri: ID!) {
		post(id: $uri, idType: URI) {
			title
			editorBlocks {
				__typename
				name
				renderedHtml
				... on CoreParagraph {
					attributes {
						className
					}
				}
			}
		}
	}
`;

export default function SinglePost() {
	const router = useRouter();
	const { slug } = router.query; // Dynamically get the slug from the URL

	const { loading, error, data } = useQuery(GET_BLOCKS_BY_URI, {
		variables: { uri: slug }, // Pass the dynamic slug to the query
		skip: !slug, // Skip the query if slug is not available yet
	});

	if (loading) return <p>Loading...</p>;
	if (error) return <p>Error: {error.message}</p>;

	const { post } = data;
	const { title, editorBlocks } = post;

	return (
		<div className="container mx-auto px-4 py-8">
			<h1 className="mb-4 text-3xl font-bold">{title}</h1>

			{/* Render the blocks */}
			<div className="prose prose-lg prose-invert">
				{editorBlocks.map((block, index) => {
					return (
						<div
							key={index}
							dangerouslySetInnerHTML={{ __html: block.renderedHtml }}
						/>
					);
				})}
			</div>
		</div>
	);
}

This code will render a single post detail page with all the block data coming from WordPress.

Next Steps

Next, you can use the template hierarchy to map block types to React components.