Advanced Custom Fields (ACF) é um dos plugins mais instalados da história do WordPress — e por boas razões. A interface é impecável, o suporte a tipos de campo é imenso, e a curva de aprendizado é zero. Mas instalar um plugin de 400KB+ com dezenas de arquivos PHP para adicionar três campos de metadado a um post customizado é, na maioria dos projetos, um canhão para matar uma mosca.
O WordPress tem suporte nativo a metadados desde a versão 1.2, e desde a Block Editor ficou substancialmente mais poderoso. Este artigo mostra como usar a API nativa para 95% dos casos de uso sem depender de nenhum plugin externo.
A base: register_meta() com tipagem e sanitização
O ponto de partida é a função register_meta(), que registra um campo de metadado com tipo, valor padrão, sanitização e, crucialmente, exposição via REST API:
add_action('init', function () {
register_meta('post', 'projeto_url', [
'object_subtype' => 'projeto', // só para o CPT 'projeto'
'type' => 'string',
'single' => true,
'default' => '',
'show_in_rest' => true, // expõe na REST API
'sanitize_callback' => 'esc_url_raw',
'auth_callback' => fn() => current_user_can('edit_posts'),
]);
register_meta('post', 'projeto_destaque', [
'object_subtype' => 'projeto',
'type' => 'boolean',
'single' => true,
'default' => false,
'show_in_rest' => true,
'auth_callback' => fn() => current_user_can('edit_posts'),
]);
});Com isso, get_post_meta($id, 'projeto_url', true) já funciona, o campo aparece na REST API em meta.projeto_url, e a sanitização é automática.
Meta boxes no editor clássico e no Gutenberg
Para exibir um campo de edição no admin, adicione uma meta box com add_meta_box(). Funciona tanto no editor clássico quanto no Gutenberg (como painel lateral):
add_action('add_meta_boxes', function () {
add_meta_box(
'projeto_detalhes',
'Detalhes do Projeto',
'render_meta_box_projeto',
'projeto',
'side',
'high'
);
});
function render_meta_box_projeto(WP_Post $post) {
wp_nonce_field('salvar_projeto_meta', 'projeto_meta_nonce');
$url = get_post_meta($post->ID, 'projeto_url', true);
$destaque = get_post_meta($post->ID, 'projeto_destaque', true);
?>
<label>URL do projeto</label>
<input type="url" name="projeto_url" value="<?= esc_attr($url) ?>" style="width:100%">
<label>
<input type="checkbox" name="projeto_destaque" value="1" <?= checked($destaque, true, false) ?>>
Projeto em destaque
</label>
<?php
}
add_action('save_post_projeto', function ($post_id) {
if (!wp_verify_nonce($_POST['projeto_meta_nonce'] ?? '', 'salvar_projeto_meta')) return;
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
update_post_meta($post_id, 'projeto_url', esc_url_raw($_POST['projeto_url'] ?? ''));
update_post_meta($post_id, 'projeto_destaque', isset($_POST['projeto_destaque']));
});Block Bindings API — o futuro no Gutenberg (WP 6.5+)
A Block Bindings API, introduzida no WordPress 6.5, permite conectar campos de metadado diretamente a atributos de blocos no editor — sem nenhum código JavaScript personalizado:
add_action('init', function () {
register_block_bindings_source('meu-plugin/meta', [
'label' => 'Metadados do Post',
'get_value_callback' => function (array $source_args, WP_Block $block) {
return get_post_meta(
get_the_ID(),
$source_args['key'] ?? '',
true
);
},
]);
});No editor, você conecta um bloco Parágrafo ao campo assim: bloco selecionado → Painel lateral → Atributos → Vincular → selecione a fonte. O conteúdo do parágrafo passa a vir dinamicamente do metadado — sem shortcodes, sem PHP inline no template.
Quando o ACF ainda faz sentido
A API nativa resolve a maioria dos casos. Mas o ACF (ou o CMB2, alternativa open source) ainda é justificado quando você precisa de:
- Campos de relacionamento entre posts (post-to-post relationships)
- Campos de repetição (Repeater Field) com subestrutura complexa
- Interface de galeria de imagens com drag-and-drop
- Grupos de campos condicionais (campo A aparece só se campo B tem valor X)
- Equipes sem perfil técnico que precisam de interface amigável no admin
Para tudo fora desse escopo — URL, texto simples, número, booleano, data, taxonomia — a API nativa é mais rápida, mais segura e não adiciona dependência externa ao projeto.
Consultando campos customizados em WP_Query
Com os metadados registrados, você pode filtrá-los em queries com meta_query:
$projetos_destaque = new WP_Query([
'post_type' => 'projeto',
'meta_query' => [[
'key' => 'projeto_destaque',
'value' => '1',
'compare' => '=',
]],
'orderby' => 'meta_value',
'meta_key' => 'projeto_url',
'order' => 'ASC',
]);Use meta_query com parcimônia em tabelas grandes — queries em wp_postmeta sem índice adequado são um dos principais culpados de lentidão em sites WordPress de médio e grande porte.