Se publica blocos próprios, ou se mantém sites de clientes onde correm blocos de outra gente, o trabalho desta semana cabe num grep e numa instalação de teste. No WordPress 7.1 o editor de artigos corre sempre dentro de um iframe. A nota de desenvolvimento de Aki Hamano, publicada a 3 de agosto de 2026, di-lo sem qualquer reserva: “Starting in WordPress 7.1, the post editor is always iframed, regardless of the theme type, the block API versions of the registered blocks, or the block API versions of the blocks in the content.” Nem o tipo de tema nem as versões da Block API alteram esse comportamento, e não existe camada de compatibilidade. Tudo o que o seu JavaScript do editor faça com o document ou o window globais para chegar ao conteúdo de um bloco passa a apontar para a página errada, e qualquer folha de estilos registada em enqueue_block_editor_assets a pensar no conteúdo deixa de se aplicar, em silêncio. O WordPress 7.1 sai a 19 de agosto de 2026, por isso o sítio certo para descobrir isto é uma release candidate.
Comece por saber se está afetado
Deixe a teoria para daqui a dez minutos. A pergunta que bloqueia todo o planeamento não exige uma auditoria, exige uma instalação, e nem sequer precisa de ser uma release candidate. A mesma nota de desenvolvimento indica um atalho: “since Gutenberg 22.6, when the plugin is active the post editor is forced to be iframed regardless of the theme type or the block API versions in use.” Com o plugin Gutenberg ativo a partir da versão 22.6, o editor de artigos é forçado a correr em iframe, seja qual for o tipo de tema e sejam quais forem as versões da Block API em uso. Um plugin num staging põe o site em condições de 7.1 hoje, sobre a versão do core que já tem instalada.
- Ative o plugin Gutenberg na versão 22.6 ou superior numa cópia de staging do site. Se preferir testar o próprio core, levante um site descartável em WordPress 7.1 RC, que dá a mesma resposta. Um contentor local chega para qualquer dos caminhos.
- Ative um plugin que registe um bloco em
apiVersion2. Serve qualquer bloco antigo do seu catálogo, ou um trazido de um site de cliente. - Abra o editor de artigos e insira esse bloco.
- Abra a consola do navegador e mantenha-a aberta enquanto trabalha com o bloco: escreva lá dentro, mude uma definição na barra lateral, salte para outro bloco e volte, e no fim apague-o.
- Leia o resultado. Leituras de propriedades em
nullno momento em que o nó é destruído, seletores a devolvernull, estilos em falta no canvas que no frontend aparecem corretos.
Duas notas sobre a interpretação. Ligue o SCRIPT_DEBUG, porque os avisos de apiVersion acrescentados na 6.9 só surgem com essa constante. E confirme os estilos com os olhos, não só pela consola, porque o encaminhamento errado de assets descrito abaixo é completamente mudo.
Dez minutos dão um sim ou um não por plugin, o que chega para dimensionar o trabalho verdadeiro. O resto do artigo trata do que fazer com um sim.
O que o iframe muda no seu código
A causa de tudo cabe numa frase da mesma nota de desenvolvimento: “the iframe has its own document and window, separate from the admin page where editor scripts run.”
O componente edit do seu bloco continua a ser renderizado pelo React a partir da página exterior do painel, mas os nós DOM que ele produz vivem dentro do iframe. Componente e marcação deixaram, portanto, de partilhar um document. Uma chamada a document.querySelector() feita a partir de dentro de um bloco interroga o post.php, não o canvas. Não lança exceção. Devolve null ou, pior ainda, devolve um nó qualquer da moldura do editor, e o código segue em frente assente numa premissa falsa.
No React não muda nada. No block.json não muda nada. O que muda é que uma classe inteira de pressupostos, até agora verdadeiros quase sempre, passa a ser falsa sempre.
As falhas e as respetivas correções
A referência é a página do manual sobre migração de blocos para compatibilidade com o iframe. Na prática, as correções têm este aspeto.
Consultas ao document global
Esta é a categoria que devolve respostas erradas em vez de erros. O issue #55947 do Gutenberg é um exemplo limpo: um script carregado por enqueue_block_editor_assets lê document.body.classList e recebe o documento exterior do post.php, pelo que a classe que procura nunca lá está.
Antes:
import { useEffect } from '@wordpress/element';
export default function Edit( { attributes } ) {
useEffect( () => {
// `document` is the admin page. This finds nothing in 7.1.
const heading = document.querySelector( '.wp-block-my-plugin-hero h2' );
if ( heading && document.body.classList.contains( 'is-dark-theme' ) ) {
heading.dataset.contrast = 'inverted';
}
}, [ attributes.style ] );
return <div className="wp-block-my-plugin-hero">{ /* ... */ }</div>;
}
Depois:
import { useRefEffect } from '@wordpress/compose';
export default function Edit( { attributes } ) {
const ref = useRefEffect(
( element ) => {
// Resolve the canvas from a node that is already inside it.
const canvas = element.ownerDocument;
const heading = element.querySelector( 'h2' );
if ( heading && canvas.body.classList.contains( 'is-dark-theme' ) ) {
heading.dataset.contrast = 'inverted';
}
},
[ attributes.style ]
);
return <div ref={ ref } className="wp-block-my-plugin-hero">{ /* ... */ }</div>;
}
Mudaram duas coisas. O element.ownerDocument devolve o documento que de facto contém o nó, pelo que o mesmo código funciona no editor de artigos, no editor do site e em qualquer superfície futura. E a procura do título ficou limitada ao elemento do próprio bloco em vez de varrer um documento inteiro, o que sempre foi a opção mais correta.
Valores lidos no window e listeners presos ao documento exterior
Tudo o que meça a partir do window sofre do mesmo mal, com um agravante: o window.innerWidth devolve um número real, apenas o número errado. É a área visível do navegador e não o canvas, que com a barra lateral de definições aberta pode ser várias centenas de pixels mais estreito.
Antes:
import { useState, useEffect } from '@wordpress/element';
export default function Edit() {
const [ width, setWidth ] = useState( 0 );
useEffect( () => {
const onResize = () => setWidth( window.innerWidth );
onResize();
window.addEventListener( 'resize', onResize );
return () => window.removeEventListener( 'resize', onResize );
}, [] );
return <div>{ width }</div>;
}
Depois:
import { useState } from '@wordpress/element';
import { useRefEffect } from '@wordpress/compose';
export default function Edit() {
const [ width, setWidth ] = useState( 0 );
const ref = useRefEffect( ( element ) => {
const view = element.ownerDocument.defaultView;
const onResize = () => setWidth( view.innerWidth );
onResize();
view.addEventListener( 'resize', onResize );
return () => view.removeEventListener( 'resize', onResize );
}, [] );
return <div ref={ ref }>{ width }</div>;
}
O element.ownerDocument.defaultView é o window que pertence ao canvas. Registe os listeners através dele, e faça-o a partir de um callback de ref e não de um efeito que não faz ideia contra que documento está a correr.
A troca de useRef mais useEffect por useRefEffect não é cosmética. O callback do useEffect não volta a ser chamado quando o ref muda, e num editor em iframe o ref muda mesmo: o nó do canvas é criado, substituído e destruído ao longo de uma sessão de edição. Um efeito que correu uma vez contra um nó entretanto desaparecido deixa listeners num documento morto e limpeza que nunca acontece. O PR #52588 do Gutenberg torna esse caso de destruição concreto, com um Cannot read properties of null (reading 'getComputedStyle') a ser lançado no momento em que o iframe desaparece.
jQuery, select2 e tudo o que procura um global
É nesta categoria que se acumula a maior parte do código mantido por agências, e é dela que trata o comentário da Meta Box citado mais à frente. O issue #47924 documenta o select2 do ACF e o datepicker do jQuery UI a partirem dentro do iframe.
Antes:
import { useEffect } from '@wordpress/element';
export default function Edit( { clientId } ) {
useEffect( () => {
jQuery( `#my-plugin-select-${ clientId }` ).select2( { width: '100%' } );
}, [ clientId ] );
return <select id={ `my-plugin-select-${ clientId }` }>{ /* ... */ }</select>;
}
Depois:
import { useRefEffect } from '@wordpress/compose';
export default function Edit() {
const ref = useRefEffect( ( element ) => {
const $ = element.ownerDocument.defaultView.jQuery;
if ( ! $ ) {
return;
}
const $select = $( element );
$select.select2( { width: '100%' } );
return () => {
$select.select2( 'destroy' );
};
}, [] );
return <select ref={ ref }>{ /* ... */ }</select>;
}
A correção assenta em três alterações. A biblioteca é obtida do window do canvas e não de um global, que é o padrão defaultView.jQuery(element). O elemento é passado diretamente em vez de ser procurado por um seletor de ID, pelo que deixa de existir procura entre documentos. E a limpeza devolvida destrói o widget quando o nó desaparece, a parte que o código existente quase sempre omite, porque antes do iframe o nó nunca desaparecia.
Se o jQuery nem sequer existir no window do canvas, o problema não é de JavaScript, é de carregamento de assets, o que nos leva à última categoria.
Estilos e scripts carregados para a superfície errada
A regra da página do manual sobre carregamento de assets no editor é uma divisão limpa. O enqueue_block_editor_assets serve a interface do editor: barras laterais, barras de ferramentas, painéis de plugins, botões de formatação. O enqueue_block_assets serve o conteúdo e alcança tanto o canvas como o frontend.
Antes:
add_action( 'enqueue_block_editor_assets', function () {
wp_enqueue_style(
'my-plugin-blocks-editor',
plugins_url( 'build/blocks.css', __FILE__ ),
array(),
'1.4.0'
);
} );
Depois:
// Editor interface only: panels, toolbars, sidebar controls.
add_action( 'enqueue_block_editor_assets', function () {
wp_enqueue_style(
'my-plugin-blocks-panels',
plugins_url( 'build/panels.css', __FILE__ ),
array(),
'1.4.0'
);
} );
// Content: applies inside the canvas and on the front end.
add_action( 'enqueue_block_assets', function () {
wp_enqueue_style(
'my-plugin-blocks-content',
plugins_url( 'build/blocks.css', __FILE__ ),
array(),
'1.4.0'
);
} );
Errar aqui não produz erro nenhum. A folha de estilos carrega, o navegador reporta 200, e as regras não encontram nada no canvas porque foram injetadas noutro documento. O issue #53236 do Gutenberg acompanha exatamente esse sintoma: uma folha de estilos carregada para o editor deixa de se aplicar ao canvas, sem dizer nada.
A mesma divisão vale para os scripts. O que tiver de correr contra nós de conteúdo tem de ser carregado onde esses nós vivem.
Porque é que nada disto se via até agora
Se o iframe existe desde a 5.8, porque parecia o seu catálogo de blocos saudável até esta semana? A resposta está no calendário, e na condição que desapareceu.
| Versão | Comportamento |
|---|---|
| 5.8 | O editor de modelos passa a correr em iframe. |
| 6.3 | O editor de artigos só corre em iframe quando todos os blocos registados declaram Block API versão 3 ou superior. O enqueue_block_assets começa a alcançar o canvas. |
| 6.9 | Surgem avisos na consola para apiVersion 2 ou inferior com SCRIPT_DEBUG. O esquema do block.json restringe os blocos novos à versão 3. |
| 7.0 | O teste estreita-se aos blocos efetivamente inseridos no artigo. A nota de desenvolvimento da 7.0, de Ella Van Durpe, diz claramente que o iframe não é imposto na 7.0. |
| 7.1 | Sempre em iframe. |
A regra da 6.3 é a origem de quase toda a confusão que se encontra no terreno. Bastava um bloco registado em apiVersion 2, em qualquer ponto da instalação, para o editor inteiro voltar ao caminho sem iframe. Um plugin podia estar completamente partido dentro do iframe sem que ninguém desse por isso, desde que um bloco antigo qualquer impedisse o iframe de entrar em jogo. Aquela condição funcionou durante anos como abafador de relatórios de erro, e a consequência é desconfortável: não ter recebido queixas não é prova de que o código está pronto.
O editor do site, esse, sempre correu em iframe. Daí que o mesmo plugin se comporte de maneiras diferentes consoante o ecrã em que a pessoa está a trabalhar.
As metaboxes clássicas perdem a isenção
Os blocos antigos não eram a única condição a segurar o iframe. Registar metaboxes clássicas era outra, e daí que tantas instalações com metaboxes à medida e grupos de campos ACF desenhados à maneira antiga nunca tenham chegado a ver o editor em iframe. O guia de campo da 7.1 encerra o assunto numa frase: “WordPress 7.1 completes the move to an iframe-based post editor, including for sites that register legacy meta boxes.” Por outras palavras, a 7.1 conclui a passagem para um editor de artigos assente em iframe, incluindo nos sites que registam metaboxes antigas.
Para quem mantém sites de terceiros, é aqui que a probabilidade se concentra, porque nada disto exige que alguém tenha escrito um bloco. Basta um tipo de conteúdo antigo com campos acrescentados por cima, e isso descreve boa parte de qualquer carteira herdada. São ecrãs a testar por si, com ronda própria: um site pode sair limpo do teste aos blocos do início do artigo e falhar mesmo assim, porque o que mudou de base foi o ecrã de edição inteiro.
Porque não existe interruptor para desligar
A fundamentação do Gutenberg PR #74042, integrado a 10 de julho de 2026, merece leitura integral, mas a frase operativa é esta: “far more breakage is caused by the inconsistency than blocks not functioning well with iframe.”
É uma decisão de engenharia defensável. Dois ambientes de renderização com uma comutação em tempo de execução são um contrato pior do que um único ambiente que às vezes é hostil. Também significa que as saídas de emergência foram fechadas: o pull request remove o caminho sem iframe, e um revisor nota que o switchToLegacyCanvas() “does nothing anymore.” Quem contava comprar mais um ciclo de lançamento com um filtro já não tem filtro a que recorrer.
A resistência é real e está documentada. No issue de acompanhamento #70743, o programador principal da Meta Box escreveu: “Switching to version 3 is impossible at the moment, as most of the JS the plugins use are using jQuery with DOM manipulation.” Não é o problema de um plugin. É a descrição de uma fatia considerável do ecossistema comercial de metaboxes e campos personalizados, sobre o qual assentam muitos dos sites institucionais e de catálogo entregues por agências em Portugal.
O que verificar numa carteira de sites
Se mantém sites que não construiu, a pergunta decisiva não é “o nosso código está pronto”, é “que código destes sites já não é responsabilidade de ninguém”. Esse inventário é trabalho de engenharia e faz-se antes de alguém da redação telefonar a dizer que o editor ficou torto.
- Construa o inventário de blocos. Por site, liste que plugins e temas registam blocos e que
apiVersioncada um declara. Subir o número não corrige comportamento na 7.1, mas a contagem de blocos em versão 2 diz onde olhar primeiro. - Separe o que é mantido do que foi abandonado. Veja a data do último lançamento de cada plugin afetado e se o autor disse alguma coisa sobre compatibilidade com o iframe. Um plugin mantido significa esperar e testar. Um abandonado significa escolher entre patch local, fork ou substituição, e essa decisão tem prazo de execução.
- Faça grep ao seu próprio código. Procure em cada plugin e tema à medida por
document.ewindow.nos bundles do editor, maisjQuery(e qualquer handler deenqueue_block_editor_assetsque registe estilos de conteúdo. Passagem rápida e mecânica, com uma lista concreta no fim. - Olhe em separado para a camada de metaboxes. O ACF, a Meta Box e ferramentas equivalentes são a categoria de risco mais alto, e estão precisamente nos sites onde a redação nota a avaria mais depressa.
- Faça uma segunda ronda nos ecrãs com metaboxes. Terminado o teste aos blocos, abra cada tipo de conteúdo com metaboxes à medida ou grupos de campos ACF clássicos, grave um artigo e confirme que os valores persistem e que os painéis continuam a desenhar-se. É uma passagem à parte, não uma alínea da primeira.
- Teste o fluxo de edição, não o carregamento da página. As falhas na destruição de nós só aparecem quando os blocos são inseridos, movidos e apagados. Um teste de fumo que abre um artigo e olha para ele passa numa instalação partida.
- Ordene o trabalho pela carga editorial. Um site cuja equipa publica todos os dias tem de estar limpo antes de 19 de agosto. Um site institucional editado duas vezes por ano não tem.
Nada disto é exótico. É um inventário, um grep e uma matriz de testes, tarefa que pertence a um ciclo planeado e não a um modo de emergência. Se preferir entregá-la num acordo contínuo, cabe num programa de manutenção WordPress, onde os testes às versões do core já estão no calendário. O valor é sempre individual, porque depende do número de sites e de quantos assentam em código que já ninguém desenvolve.
Fontes
- Aki Hamano, Iframed Editor Changes in WordPress 7.1, 3 de agosto de 2026
- Ella Van Durpe, Iframed Editor Changes in WordPress 7.0, 24 de fevereiro de 2026
- WordPress 7.1 Field Guide, 5 de agosto de 2026
- Aki Hamano, Preparing the Post Editor for Full iframe Integration, 12 de novembro de 2025
- Block migration for iframe editor compatibility, Block Editor Handbook
- Enqueueing assets in the editor, Block Editor Handbook
- Gutenberg PR #74042, “Post editor: always iframe”, integrado a 10 de julho de 2026
- Gutenberg, issue de acompanhamento #70743 e issues #55947, #47924, #53236, PR #52588






