Block Making
concepts for making blocks in this project
Demonstration
File Structure
This is the file structure from create-react-block. You can copy an existing block for convenience.
Register Block in index.tsx
import { registerBlockType } from "@wordpress/blocks";
import { __ } from "@wordpress/i18n";
import "./style.scss";
import edit from "./edit";
import save from "./save";
import { PREFIX } from "../../utils/config";
registerBlockType("gravity-platform-core/promotions", {
title: __("Promotions", PREFIX),
category: "gravity-platform",
attributes: {
formId: {
type: "number",
default: 1,
},
selectedPromotionIds: {
type: "array",
default: [],
},
},
edit,
save,
});
Create Custom Post Type - if needed
function gravity_platform_core_blocks_init()
{
register_block_type(__DIR__ . '/src/blocks/showroom');
register_block_type(__DIR__ . '/src/blocks/promotions');
register_post_type( 'promotions',
array(
'labels' => array(
'name' => __( 'Promotions' ),
'singular_name' => __( 'Promotion' )
),
'public' => true,
'has_archive' => true,
'rewrite' => array('slug' => 'promotions'),
'show_in_rest' => true,
'show_in_graphql' => true,
'hierarchical' => true,
'graphql_single_name' => 'promotion',
'graphql_plural_name' => 'promotions',
)
);
}
add_action('init', 'gravity_platform_core_blocks_init');
Build Your Query - if using a postType for your block
You can use the GraphQl IDE in the Wordpress Backend to build your query.

import { gql } from "@apollo/client";
/**
* GraphQL countries query.
*/
const GET_PROMOTIONS = gql`query GET_PROMOTIONS{
promotions {
nodes {
title
slug
fields {
description
disclaimer
endDate
subtitle
summary
image {
altText
sourceUrl
}
}
}
}
}`;
export default GET_PROMOTIONS;
Make Your Frontend Block
/**
* Importing React is required for use in Gutenberg since WP Scripts doesn't compile components
* as UMD modules (React is injected as a UMD module automatically in Next.js environments without an import)
*/
import React from "react";
import client from "../../../lib/wp/connector";
import GET_PROMOTIONS_QUERY from "../../../lib/wp/queries/get-promotions";
import Link from "next/link";
type Props = {
promotions: any[];
};
export default function PromotionsBlock({ promotions }: Props) {
return (
<section className="text-gray-700 ">
<div className="container items-center px-5 py-8 mx-auto lg:px-24">
<div className="flex flex-wrap mb-12 text-left">
{
promotions.map( promotion => (
<div className="w-full p-6 mx-auto lg:w-1/3">
<div className="shadow-xl rounded-xl bg-gray-50">
<img className="object-cover object-center w-full lg:h-48 md:h-36 rounded-t-xl" src={'http://' + promotion.fields.image.sourceUrl.substring(8)} alt="blog" />
<div className="p-4 lg:p-8 bg-gray-50">
<h1 className="mx-auto mb-4 text-2xl font-semibold leading-none tracking-tighter text-black lg:text-3xl title-font">{ promotion.title }</h1>
<h2 className="mb-8 text-xs font-semibold tracking-widest text-black uppercase title-font" dangerouslySetInnerHTML={{__html: promotion.fields.subtitle}}></h2>
</div>
<div className="px-6 py-4 bg-gray-100 rounded-b-xl">
<Link href={'promotions/' + promotion.slug}>
<a className="px-4 py-1 mr-1 text-base text-gray-500 transition duration-500 ease-in-out transform rounded-md focus:shadow-outline focus:outline-none focus:ring-2 ring-offset-current ring-offset-2 hover:text-black ">
Details »
</a>
</Link>
<a href="#" className="inline-flex items-center mt-auto font-semibold text-blue-600 lg:mb-0 hover:text-black " title="Read Full Article"> </a>
</div>
</div>
</div>
)
)
}
</div>
</div>
</section>
);
}
const getPropsData = async () => {
const { data } = await client.query({
query: GET_PROMOTIONS_QUERY
});
return {
props: {
promotions: data?.promotions?.nodes ?? []
}
};
};
/**
* Called from WP's block preview screen.
* Separate from getServerSideProps because private/session based data might need to be mocked.
* @returns
*/
export const getPreviewProps = async () => {
return await getPropsData();
};
/**
* Called by the page the block exists on within it's getServerSideProps method.
* The result is passed to the component props.
* @returns
*/
export const getServerSideProps = async () => {
return await getPropsData();
};
When creating & registering your block the naming convention is to kabab-case rather than TitleCase or camelCase.
Detail Page with Dynamic Routes
import { GetServerSideProps } from "next";
import { ReactElement } from "react";
import Layout from "../../components/Layout";
import client from "../../lib/wp/connector";
import { PROMOTION_BY_SLUG_QUERY } from "../../lib/wp/queries/promotion-by-slug";
import { getSiteByDomain } from "../../lib/sites";
type Props = {
promotion: any;
};
export default function PromotionDetail({ promotion }: Props): ReactElement {
return (
<Layout title={promotion.title}>
<section className="text-gray-600 body-font">
<div className="container mx-auto flex px-5 py-24 md:flex-row flex-col items-center">
<div className="lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center">
<h1 className="title-font sm:text-4xl text-3xl mb-4 font-medium text-gray-900">{promotion.fields.subtitle}</h1>
<p className="mb-8 leading-relaxed" dangerouslySetInnerHTML={{__html: promotion.fields.summary}}></p>
<p className="text-sm mt-2 text-gray-500 mb-8 w-full" dangerouslySetInnerHTML={{__html: promotion.fields.description}}></p>
</div>
<div className="lg:max-w-lg lg:w-full md:w-1/2 w-5/6">
<img className="object-cover object-center w-full lg:h-48 md:h-36 rounded-t-xl" src={'http://' + promotion.fields.image.sourceUrl.substring(8)} alt={promotion.title} />
</div>
</div>
</section>
</Layout>
);
}
export const getServerSideProps: GetServerSideProps = async ({
req,
res,
resolvedUrl
}) => {
res.setHeader(
"Cache-Control",
"public, s-maxage=1, stale-while-revalidate=86399" // 24 hours
);
// allow self-signed certs when developing locally
if (process.env.NODE_ENV === "development") {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
}
const site = getSiteByDomain(req.headers.host);
if (!site) {
return {
notFound: true
};
}
const uri = resolvedUrl;
const { data } = await client.query({
query: PROMOTION_BY_SLUG_QUERY,
variables: { uri }
});
return {
props: {
promotion: data?.promotion
}
};
};
Query Details for Single Item
import { gql } from "@apollo/client";
export const PROMOTION_BY_SLUG_QUERY = gql `
query Promotion($uri: ID!) {
promotion(id: $uri, idType: URI) {
id
slug
title
fields {
description
disclaimer
endDate
subtitle
summary
image {
altText
sourceUrl
}
}
}
}
`
Last updated
Was this helpful?