Migrasi Generic Heading & Generic Button Blocks
Gambaran Umum
Generic heading dan generic button block berbeda dari block lainnya: mereka tidak memiliki render.php. Block ini menyimpan HTML statis langsung di database — tidak ada dynamic rendering di server. Ini lebih cepat tapi kurang fleksibel.
Mengapa Tanpa render.php?
Block DENGAN render.php (banner, slide, slideshow, footer, header...):
→ Database menyimpan atribut/InnerBlocks
→ Server menjalankan PHP setiap request
→ HTML dihasilkan secara dinamis
Block TANPA render.php (generic heading, generic button):
→ Database menyimpan HTML statis lengkap
→ Server langsung menyajikan HTML dari database
→ Lebih cepat, tapi tidak bisa membuat keputusan dinamisGeneric Heading Block
Langkah 1: functions.php
// Hapus class JSXBlock sepenuhnya (sudah tidak dipakai)
// Hapus: new JSXBlock("genericheading", false);
// Tambahkan di ourNewBlocks():
register_block_type_from_metadata(__DIR__ . "/build/genericheading");Langkah 2: Buat folder src/genericheading/
Duplikasi folder sederhana (misal src/archive/) → rename jadi genericheading.
PENTING: Hapus render.php dari folder ini! Block ini tidak memiliki server-side rendering.
Langkah 3: block.json (TANPA render property!)
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "ourblocktheme/genericheading",
"title": "Generic Heading",
"attributes": {
"text": {
"type": "string"
},
"size": {
"type": "string",
"default": "large"
}
},
"editorScript": "file:./index.js"
}Perhatikan:
- TIDAK ADA properti
"render"— jika ada, akan error karena file tidak ditemukan - Attributes
textdansizedipindahkan dari JavaScript lama keblock.json
Langkah 4: edit.js
import {
RichText,
BlockControls,
useBlockProps
} from "@wordpress/block-editor";
import { ToolbarGroup, ToolbarButton } from "@wordpress/components";
export default function Edit(props) {
const blockProps = useBlockProps();
function handleTextChange(x) {
props.setAttributes({ text: x });
}
return (
<div {...blockProps}>
<BlockControls>
<ToolbarGroup>
<ToolbarButton
isPressed={props.attributes.size === "large"}
onClick={() => props.setAttributes({ size: "large" })}
>
Large
</ToolbarButton>
<ToolbarButton
isPressed={props.attributes.size === "medium"}
onClick={() => props.setAttributes({ size: "medium" })}
>
Medium
</ToolbarButton>
<ToolbarButton
isPressed={props.attributes.size === "small"}
onClick={() => props.setAttributes({ size: "small" })}
>
Small
</ToolbarButton>
</ToolbarGroup>
</BlockControls>
<RichText
allowedFormats={["core/bold", "core/italic"]}
tagName="h1"
className={`headline headline--${props.attributes.size}`}
value={props.attributes.text}
onChange={handleTextChange}
/>
</div>
);
}Penting API Version 3:
- JSX dibungkus
<div {...blockProps}>— bukan langsung<RichText> - Wrapper div menggantikan fungsi React Fragment dari versi lama
- WordPress butuh wrapper ini untuk mendeteksi seleksi/klik block
Langkah 5: index.js (dengan Save Function)
import { registerBlockType } from "@wordpress/blocks";
import { RichText } from "@wordpress/block-editor";
import metadata from "./block.json";
import Edit from "./edit";
function Save(props) {
function createTagName() {
switch (props.attributes.size) {
case "large":
return "h1";
case "medium":
return "h2";
case "small":
return "h3";
}
}
return (
<RichText.Content
tagName={createTagName()}
value={props.attributes.text}
className={`headline headline--${props.attributes.size}`}
/>
);
}
registerBlockType(metadata.name, {
edit: Edit,
save: Save,
});Perbedaan save function ini vs block lain:
- Save function punya logika nyata (bukan hanya
<InnerBlocks.Content />) RichText.Contentmenghasilkan HTML statis yang disimpan di databasecreateTagName()menentukan tag HTML (h1/h2/h3) berdasarkan ukuran
Langkah 6: Update Template Files
Karena API Version 3 menambahkan wrapper div, class di template .html perlu diperbarui:
Sebelum (front-page.html):
<!-- wp:ourblocktheme/genericheading {"text":"...","size":"large"} -->
<h1 class="wp-block-ourblocktheme-genericheading headline headline--large">...</h1>
<!-- /wp:ourblocktheme/genericheading -->Sesudah:
<!-- wp:ourblocktheme/genericheading {"text":"...","size":"large"} -->
<h1 class="headline headline--large">...</h1>
<!-- /wp:ourblocktheme/genericheading -->→ Hapus wp-block-ourblocktheme-genericheading dari semua instance di template files.
Jika masih muncul error: Clear Customizations di editor (WordPress icon → 3 dots → Clear Customizations) agar menggunakan file template dari hard drive, bukan database.
Generic Button Block
Langkah 1: functions.php
// Hapus: new JSXBlock("genericbutton", false);
// Hapus seluruh class JSXBlock (sudah tidak diperlukan sama sekali)
// Tambahkan di ourNewBlocks():
register_block_type_from_metadata(__DIR__ . "/build/genericbutton");Langkah 2: Duplikasi folder
Duplikasi src/genericheading/ → rename jadi genericbutton. Pastikan tidak ada render.php.
Langkah 3: block.json
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "ourblocktheme/genericbutton",
"title": "Generic Button",
"attributes": {
"text": {
"type": "string"
},
"size": {
"type": "string",
"default": "large"
},
"linkObject": {
"type": "object",
"default": {
"url": ""
}
},
"colorName": {
"type": "string",
"default": "blue"
}
},
"editorScript": "file:./index.js"
}Langkah 4: edit.js
import { RichText, InspectorControls, BlockControls, useBlockProps } from "@wordpress/block-editor";
import { PanelBody, PanelRow, ColorPalette, ToolbarGroup, ToolbarButton, Popover } from "@wordpress/components";
import { __experimentalLinkControl as LinkControl } from "@wordpress/block-editor";
import { useState } from "@wordpress/element";
import ourColors from "../../inc/ourColors";
import { link } from "@wordpress/icons";
export default function Edit(props) {
const blockProps = useBlockProps();
const [isLinkPickerVisible, setIsLinkPickerVisible] = useState(false);
function handleTextChange(x) {
props.setAttributes({ text: x });
}
function buttonHandler() {
setIsLinkPickerVisible((prev) => !prev);
}
function handleLinkChange(newLink) {
props.setAttributes({ linkObject: newLink });
}
// ... (sisanya sama dengan genericbutton.js lama)
return (
<div {...blockProps}>
<BlockControls>
<ToolbarGroup>
<ToolbarButton onClick={buttonHandler} icon={link} />
</ToolbarGroup>
<ToolbarGroup>
<ToolbarButton
isPressed={props.attributes.size === "large"}
onClick={() => props.setAttributes({ size: "large" })}
>
Large
</ToolbarButton>
<ToolbarButton
isPressed={props.attributes.size === "medium"}
onClick={() => props.setAttributes({ size: "medium" })}
>
Medium
</ToolbarButton>
</ToolbarGroup>
</BlockControls>
<InspectorControls>
<PanelBody title="Color" initialOpen={true}>
<PanelRow>
<ColorPalette
disableCustomColors={true}
clearable={false}
colors={ourColors}
value={props.attributes.colorName}
onChange={(x) => props.setAttributes({ colorName: x })}
/>
</PanelRow>
</PanelBody>
</InspectorControls>
<RichText
allowedFormats={[]}
tagName="a"
className={`btn btn--${props.attributes.size} btn--${props.attributes.colorName}`}
value={props.attributes.text}
onChange={handleTextChange}
/>
{isLinkPickerVisible && (
<Popover position="middle center" onFocusOutside={() => setIsLinkPickerVisible(false)}>
<LinkControl
settings={[]}
value={props.attributes.linkObject}
onChange={handleLinkChange}
/>
<br /><br />
</Popover>
)}
</div>
);
}Path import ourColors:
src/genericbutton/edit.js → ../../inc/ourColorsHarus naik dua level karena berada di src/genericbutton/.
Langkah 5: index.js (dengan Save)
import { registerBlockType } from "@wordpress/blocks";
import metadata from "./block.json";
import Edit from "./edit";
function Save(props) {
return (
<a
href={props.attributes.linkObject.url}
className={`btn btn--${props.attributes.size} btn--${props.attributes.colorName}`}
>
{props.attributes.text}
</a>
);
}
registerBlockType(metadata.name, {
edit: Edit,
save: Save,
});Setelah Semua Selesai: Hapus Folder Lama
Hapus folder our-blocks/ sepenuhnya!
Semua block sudah dimigrasi ke src/ folder.Hapus Class PHP yang Tidak Dipakai
// Hapus dari functions.php:
// - Seluruh class PlaceholderBlock ← sudah dihapus sebelumnya
// - Seluruh class JSXBlock ← hapus sekarangRingkasan: Block Tanpa render.php
| Aspek | Dengan render.php | Tanpa render.php |
|---|---|---|
| Save function | <InnerBlocks.Content /> | Logika penuh (RichText.Content, dll) |
| HTML di database | Minimal (inner blocks) | HTML statis lengkap |
| Dynamic rendering | ✅ PHP setiap request | ❌ Langsung dari database |
| Performance | Sedikit lebih lambat | Sedikit lebih cepat |
| Contoh | banner, slide, footer | generic heading, generic button |
Error Umum yang Sering Muncul
| Error | Solusi |
|---|---|
useBlockProps is not defined | Import dari @wordpress/block-editor |
props is not defined | Tambahkan props di parameter fungsi Edit |
| Block validation failed | Update class di template .html files, lalu Clear Customizations |
render.php not found | Hapus property "render" dari block.json |