Este post é a parte 13 de 13 da Série WordPress Extremo
Série
WordPress Extremo
WordPress Extremo
- Como Criar um Plugin WordPress com Composer e PSR-4 – WordPress Extremo Dia 1
- Como Usar Serviços em Plugins para Código Limpo e Desacoplado – WordPress Extremo Dia 2
- Como Usar Repositórios em Plugins para Separar Lógica de Dados – WordPress Extremo Dia 3
- Injeção de Dependência Manual em Plugins WordPress – WordPress Extremo Dia 4
- Hooks Avançados no WordPress: apply_filters, do_action e boas práticas
- Criando Comandos WP-CLI Personalizados para Plugins WordPress
- Criando Blocos Personalizados com Gutenberg e React
- Estilizando Blocos Gutenberg com CSS e Classes Dinâmicas
- Cor, Alinhamento e Estilo Dinâmico com Gutenberg + React
- Ícones, Imagens e Classes Personalizadas no Gutenberg
- Componentes Reutilizáveis e Atributos Compostos em Blocos Gutenberg
- Conectando Blocos com a REST API do WordPress
- Bloco Dinâmico com Renderização no Servidor
Neste dia, exploramos como criar um bloco dinâmico que se adapta tanto no editor quanto no frontend, utilizando atributos do Gutenberg e a função render_callback()
no PHP para gerar o HTML final — o famoso bloco com renderização via servidor.
✍️ O que você vai aprender:
- Criar um bloco com lista de posts via REST API no editor.
- Exibir os posts de forma dinâmica no frontend com PHP.
- Separar responsabilidades: JS (editor) e PHP (renderização).
- Evitar erros de conflito de blocos duplicados.
- Estruturar o código com qualidade de produção.
✅ Pré-requisitos:
- Plugin
wp-arquitetura-extrema
já iniciado. - Pasta
blocks/posts
criada. - WP-CLI, Composer e @wordpress/scripts funcionando.
- Webpack configurado no
webpack.config.js
.
🛠️ Estrutura do bloco posts
:
blocks/
└── posts/
├── block.json
├── edit.js
├── save.js
├── index.js
└── components/
└── PostList.js
🔁 Código do Bloco
📦 block.json
{
"apiVersion": 2,
"name": "wp24h/posts",
"title": "Lista de Posts WP24H",
"category": "widgets",
"icon": "list-view",
"description": "Um bloco que exibe os últimos posts via REST API.",
"attributes": {
"quantidade": {
"type": "number",
"default": 3
}
},
"supports": {
"html": false
}
}
🧠 edit.js
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, RangeControl } from '@wordpress/components';
import { useState, useEffect } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import PostList from './components/PostList';
export default function Edit({ attributes, setAttributes }) {
const { quantidade } = attributes;
const [posts, setPosts] = useState([]);
useEffect(() => {
apiFetch({ path: `/wp/v2/posts?per_page=${quantidade}` })
.then(setPosts);
}, [quantidade]);
return (
<>
<InspectorControls>
<PanelBody title="Configurações">
<RangeControl
label="Quantidade de posts"
min={1}
max={10}
value={quantidade}
onChange={(val) => setAttributes({ quantidade: val })}
/>
</PanelBody>
</InspectorControls>
<div {...useBlockProps()}>
<PostList posts={posts} />
</div>
</>
);
}
💾 save.js
import { useBlockProps } from '@wordpress/block-editor';
export default function save() {
return null; // Usamos render_callback no PHP
}
🧩 index.js
import { registerBlockType } from '@wordpress/blocks';
import edit from './edit';
import save from './save';
import metadata from './block.json';
registerBlockType(metadata.name, {
...metadata,
edit,
save,
});
🧷 components/PostList.js
export default function PostList({ posts }) {
if (!posts.length) return <p>Carregando posts...</p>;
return (
<ul className="wp24h-post-list">
{posts.map(post => (
<li key={post.id}>
<strong>{post.title.rendered}</strong>
</li>
))}
</ul>
);
}
⚙️ PHP — inc/register/posts.php
<?php
function wp24h_render_block_posts($attributes) {
$qtd = isset($attributes['quantidade']) ? intval($attributes['quantidade']) : 3;
$query = new WP_Query([
'post_type' => 'post',
'posts_per_page' => $qtd,
]);
if (!$query->have_posts()) {
return '<p>Sem posts recentes no momento.</p>';
}
ob_start();
echo '<ul class="wp24h-post-list-frontend">';
while ($query->have_posts()) {
$query->the_post();
echo '<li><a href="' . get_permalink() . '">' . get_the_title() . '</a></li>';
}
echo '</ul>';
wp_reset_postdata();
return ob_get_clean();
}
add_action('init', function () {
$block_path = plugin_dir_path(__FILE__) . '../../blocks/posts';
$script_path = plugin_dir_path(__FILE__) . '../../dist/posts.asset.php';
if (!file_exists($script_path)) return;
$asset = include $script_path;
wp_register_script(
'wp24h-posts',
plugins_url('../../dist/posts.js', __FILE__),
$asset['dependencies'],
$asset['version']
);
register_block_type($block_path, [
'editor_script' => 'wp24h-posts',
'render_callback' => 'wp24h_render_block_posts',
]);
});
🔁 Encaixe no seu plugin:
No wp-arquitetura-extrema.php
:
require_once plugin_dir_path(__FILE__) . 'inc/register/posts.php';
E no Init.php
, remova 'posts'
da lista de blocos:
$blocks = ['hello', 'card'];
🧱 Webpack
Garanta que o webpack.config.js
está configurado para gerar múltiplos blocos:
const path = require('path');
const defaultConfig = require('@wordpress/scripts/config/webpack.config');
module.exports = {
...defaultConfig,
entry: {
hello: path.resolve(__dirname, 'blocks/hello/index.js'),
card: path.resolve(__dirname, 'blocks/card/index.js'),
posts: path.resolve(__dirname, 'blocks/posts/index.js'),
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
clean: false
},
};
✅ Resultado
Agora você tem um bloco 100% funcional, que:
- Exibe posts no editor (JS com REST API).
- Renderiza no frontend com HTML puro via PHP (
render_callback
). - Está separado, modular e pronto pra produção.
📣 CTA final
Se você curtiu esse conteúdo e quer se tornar um desenvolvedor WordPress de elite, conheça:
- 🧭 Mentorias e Cursos
- 💬 Deixe dúvidas ou sugestões nos comentários!