12k
All articles

htmx 4.0 Chegou

htmx 4.0 muda herança, swap de erros, histórico e eventos, com dicas de migração e opções de reversão para apps htmx 2.

OpenReplay Team
OpenReplay Team
htmx 4.0 Chegou

O htmx 4.0.0 foi lançado em 28 de agosto de 2026. Ele altera vários comportamentos padrão de longa data: a herança de atributos agora é opt-in, respostas de erro passam a ser inseridas no DOM (swap) e o cache de snapshots do histórico foi removido.

Se você mantém uma aplicação htmx 2, a pergunta prática é se aquele hx-confirm que você colocou em um contêiner dois anos atrás ainda protege alguma coisa depois do upgrade. Não protege, a menos que você adicione um modificador. Este artigo cobre o que quebra, o que reverte cada quebra e por que a estratégia de release no npm significa que você provavelmente não precisa agir esta semana. Para entender o que é htmx e por que hipermídia, o tutorial do htmx 2.0 cobre a base a partir da qual este artigo começa.

Principais Pontos

  • O htmx 4 torna a herança de atributos explícita por meio do modificador :inherited, e definir htmx.config.implicitInheritance como true restaura o comportamento do htmx 2 como uma ponte de migração.
  • Apenas respostas 204 e 304 ignoram o swap por padrão no htmx 4, portanto um 422 renderizado pelo servidor agora chega ao target em vez de ser descartado; htmx.config.noSwap = [204, 304, '4xx', '5xx'] reverte esse comportamento.
  • Os nomes de eventos seguem o padrão htmx:phase:action, que não possui chave de configuração: todos os listeners htmx no seu JavaScript precisam ser renomeados, ou você instala a extensão htmx-2-compat.
  • Renomeie hx-disable para hx-ignore antes de fazer o upgrade, porque o htmx 4 reatribui o nome hx-disable à função que o hx-disabled-elt exercia.
  • O htmx 2.x mantém a tag latest no npm enquanto o 4.0 fica sob next, então URLs de CDN sem versão não são atualizadas à força, e o anúncio declara que o htmx 2 será suportado indefinidamente.

O Que Mudou no htmx 4?

O htmx 4 migra as entranhas de requisição da biblioteca de XMLHttpRequest para fetch(), e essa reescrita foi o que tornou o resto do release possível. Trocar o transporte já era uma breaking change, então a equipe aproveitou a mesma versão major para redefinir padrões que haviam se acumulado desde o htmx 1.

Você não chama nenhuma dessas APIs diretamente ao escrever htmx, então a troca de transporte em si é invisível nos seus templates. As consequências aparecem nas bordas: os eventos de ciclo de vida específicos do XHR não têm equivalente em fetch() e foram removidos, e o htmx 4 define htmx.config.defaultTimeout como 60000, enquanto o htmx 2 deixava uma requisição pendurada indefinidamente.

Sobre o número da versão: o criador do htmx, Carson Gross, havia dito que nunca haveria um htmx 3, então o release pula direto para o 4.0 e a promessa sobrevive por um detalhe técnico. Ele expôs o raciocínio no ensaio que anunciou a reescrita, em novembro de 2025.

A Herança de Atributos Agora É Explícita

No htmx 4, a herança só acontece quando você a solicita. Um atributo em um contêiner se aplica apenas àquele contêiner, a menos que você adicione o modificador :inherited, e o modificador funciona em qualquer atributo: hx-boost:inherited, hx-target:inherited, hx-confirm:inherited.

<!-- htmx 4: the confirm reaches both buttons -->
<div hx-confirm:inherited="Are you sure?">
  <button hx-delete="/account">Delete My Account</button>
  <button hx-put="/account">Update My Account</button>
</div>

Um valor definido em um elemento filho prevalece sobre o herdado, por padrão. Use :append quando quiser combinar os dois, que é o caso de composição em que as pessoas costumam tropeçar:

<div hx-vals:inherited="tenant:acme">
  <button hx-post="/save" hx-vals:append="source:save-btn">Save</button>
</div>

Sem :append, o hx-vals próprio do botão toma o lugar do herdado e tenant nunca chega ao servidor. Onde nenhum ancestral define o atributo, o valor anexado é o único valor enviado. Os nomes variam um pouco conforme o atributo: a página de referência de hx-disable documenta :merge para a mesma função de somar ao valor de um elemento pai.

hx-inherit e hx-disinherit foram removidos, já que o opt-in explícito torna ambos desnecessários. Se seus templates dependem do comportamento antigo, defina htmx.config.implicitInheritance como true para restaurá-lo enquanto você migra. Trate isso como uma ponte, não como destino final.

Respostas de Erro Sofrem Swap por Padrão

No htmx 4, uma resposta chega ao target independentemente do seu status code, com apenas 204 e 304 sendo retidos. Uma página de validação 422 renderizada pelo servidor agora chega ao target em vez de ser silenciosamente descartada, que é o que aplicações hipermídia sempre quiseram. Uma resposta de erro HTTP também dispara um evento htmx:response:error.

O novo atributo hx-status roteia códigos individuais para seus próprios target e swap:

<form hx-post="/submit"
      hx-target="#result"
      hx-status:422="target:#validation-errors"
      hx-status:5xx="target:#server-error"
      hx-status:503="swap:none">
  <input name="email">
  <button type="submit">Submit</button>
</form>

O htmx tenta primeiro o padrão mais específico: o código exato, depois um padrão com o último dígito mascarado, como 50x, e então um com os dois últimos mascarados, como 5xx. Dentro do valor do atributo você pode definir swap:, target:, select:, push:, replace: e transition:.

Se o seu backend retorna páginas de erro que nunca foram projetadas para sofrer swap, defina htmx.config.noSwap como [204, 304, '4xx', '5xx'] e você terá de volta o comportamento do htmx 2.

A Navegação para Trás Agora É uma Requisição de Verdade

O htmx 4 abandona o cache de snapshots do DOM no lado do cliente que sustentava o histórico no htmx 2. Pressione voltar e o htmx pede a página ao servidor novamente, então insere o que retorna no <body>, ou em um elemento [hx-history-elt] onde a página tiver um.

O efeito prático é que o botão voltar mostra o que o servidor diz que a página é atualmente, e não um snapshot congelado no momento em que você saiu. Isso elimina toda uma classe de bugs em que scripts de terceiros mutavam o DOM e o snapshot restaurado reproduzia essas mutações, levando a um estado quebrado. Também significa que a navegação para trás custa uma requisição.

O atributo hx-history foi embora junto com o cache. Se você precisa de snapshots, a extensão core hx-history-cache os reintroduz como opt-in.

Nomes de Eventos Seguem o Padrão htmx:phase:action

Todos os eventos de ciclo de vida do htmx foram renomeados para o formato htmx:phase:action[:sub-action]. O anúncio apresenta htmx:beforeRequest como htmx:before:request e htmx:beforeSwap como htmx:before:swap; htmx:afterSwap se torna htmx:after:swap.

Essa é a única mudança sem válvula de escape via configuração. Todo listener precisa ser editado:

// htmx 2
document.body.addEventListener('htmx:afterSwap', (e) => {
  initTooltips(e.detail.target);
});

// htmx 4
document.body.addEventListener('htmx:after:swap', (e) => {
  initTooltips(e.detail.target);
});

A maioria dos eventos de erro foi consolidada em htmx:error, com respostas de erro HTTP disparando htmx:response:error. Os eventos específicos do XHR simplesmente desapareceram, já que fetch() não expõe equivalentes. Se editar listeners manualmente representa o grosso da sua migração, a extensão htmx-2-compat mapeia os nomes antigos de eventos para os novos e também restaura a herança implícita e o hx-ext.

O Que Há de Novo no htmx 4, em Vez de Quebrado?

Três adições do htmx 4 justificam o upgrade por si só: morph swaps, o elemento <hx-partial> e as extensões de streaming reescritas. Morph swaps estão no core, então atualizações de DOM que preservam estado não precisam mais de uma extensão. O elemento <hx-partial> permite que uma única resposta atualize vários targets, cada um carregando seu próprio target e swap:

<hx-partial hx-target="#messages" hx-swap="beforeend">
  <div>New message</div>
</hx-partial>
<hx-partial hx-target="#count">
  <span>5</span>
</hx-partial>

Como o target e o estilo de swap ficam no próprio partial, a resposta declara o que quer que seja feito com cada pedaço, em vez de deixar você deduzir isso a partir de atributos hx-swap-oob espalhados pela marcação. Observe que a ordem dos swaps out-of-band foi invertida no htmx 4: o conteúdo principal é inserido primeiro.

As extensões de streaming são o outro destaque. Tanto a extensão SSE quanto a de WebSocket foram reconstruídas para este release, e um conjunto novo é distribuído junto: hx-multipart, hx-live, hx-targets, hx-ptag, hx-csp, hx-download, hx-prompt e hx-history-cache. Os atributos de conexão são namespaced, então SSE conecta com hx-sse:connect e WebSockets com hx-ws:connect.

Atualizando para o htmx 4, e Por Que Não Há Pressa

Atualizar para o htmx 4 começa pelo scanner: execute-o antes de planejar qualquer coisa. npx htmx.org@4.0.0 upgrade-check -- ./path/to/project/root percorre seu projeto e imprime cada padrão obsoleto com seu arquivo e número de linha, o que é suficiente para dimensionar o trabalho em uma tarde.

npx htmx.org@4.0.0 upgrade-check -- ./templates
npx htmx.org@4.0.0 upgrade-check --ext .vue ./path/to/project/root

Por padrão, ele examina .html, .php, .js, .ts, .jinja, .jinja2, .j2, .erb e .hbs. Formatos de componentes single-file não estão nesse conjunto, então templates .vue, .svelte, .jsx e .astro ficam sem verificação, a menos que você passe --ext.

Faça uma renomeação antes de tocar em qualquer outra coisa: hx-disable se torna hx-ignore, e hx-disabled-elt se torna hx-disable. O nome antigo é reaproveitado para outra função, então, se você migrar hx-disabled-elt primeiro, vai sobrescrever atributos que ainda significam o que significavam no htmx 2.

MudançaPadrão no htmx 4O que restaura o htmx 2
Herança de atributosExplícita, via :inheritedhtmx.config.implicitInheritance = true
Swap de respostas de erroApenas 204/304 ignoram o swaphtmx.config.noSwap = [204, 304, '4xx', '5xx']
HistóricoNova busca no servidor ao voltarExtensão hx-history-cache
Nomes de eventoshtmx:phase:actionSem chave de configuração; extensão htmx-2-compat

E então a parte que decide se algo disso é urgente: no npm, o htmx 2.x detém a dist-tag latest e o 4.0.0 está publicado sob next. O anúncio é explícito ao dizer que isso é deliberado, para que sites que carregam o htmx a partir de uma URL de CDN sem versão não sejam atualizados à força para breaking changes, com o 2.x permanecendo como latest até o início de 2027. O 2.x continua suportado indefinidamente.

Isso se traduz em quatro posições. Uma URL de CDN sem versão continua servindo o 2.x até a tag mudar, que é o único caso com um prazo futuro associado. Uma URL de CDN fixada em uma versão e um pin exato no npm nunca mudam sozinhos. Um range npm como ^2.0.0 permanece dentro do 2.x independentemente das dist-tags. Para instalar o 4.0 hoje, fixe a versão: npm install htmx.org@4.0.0, ou use o caminho versionado da CDN.

Comece projetos novos no 4.0. Para uma aplicação htmx 2 existente, rode o scanner, faça primeiro a renomeação do hx-disable e decida, pelo tamanho do relatório, se migra agora ou se revisita o assunto antes que a dist-tag mude.

Perguntas Frequentes

Como carrego uma extensão htmx no htmx 4 agora que hx-ext foi removido?

Inclua o script da extensão depois do script do htmx e seus atributos funcionam imediatamente, sem necessidade de um atributo de ativação. Carregue dist/ext/hx-sse.js junto com htmx.min.js e você pode usar hx-sse:connect diretamente. A distribuição htmax.js traz o htmx pré-empacotado com as extensões mais populares em um único arquivo, com esses atributos automaticamente disponíveis. Autores de extensões registram através de htmx.registerExtension, com um nome e um mapa de métodos.

Posso evitar a requisição extra ao servidor que o htmx 4 faz na navegação para trás?

Sim. A extensão core hx-history-cache restaura o histórico a partir do sessionStorage em vez de emitir uma requisição completa ao servidor, o que é o equivalente mais próximo dos snapshots do htmx 2. Dois valores de configuração alteram o comportamento de outra forma: htmx.config.history definido como 'reload' faz um reload completo da página na navegação de histórico, e htmx.config.history definido como false desativa o tratamento de histórico. O cache de snapshots em localStorage do htmx 2 não existe mais.

O que substitui hx-vars e hx-prompt no htmx 4?

hx-vars foi removido, e valores computados passam para hx-vals com o prefixo js:. hx-prompt foi removido do core e é distribuído como extensão: carregue a extensão hx-prompt para manter a mesma sintaxe. Outros atributos removidos incluem hx-ext, hx-inherit, hx-disinherit e hx-history. hx-disabled-elt foi renomeado, e não removido: ele se torna hx-disable, e o antigo hx-disable se torna hx-ignore, como estabelece a tabela de renomeações em [What's New in htmx 4](https://four.htmx.org/docs/whats-new-in-htmx-4). O scanner upgrade-check marca esses dois como renamed-attr e as remoções genuínas como removed-attr, cada uma com o arquivo, o número da linha e a substituição sugerida.

hx-swap-oob ainda funciona no htmx 4, e quando devo usar hx-partial em vez dele?

hx-swap-oob ainda funciona, mas o htmx 4 inverte a ordem: o conteúdo principal entra primeiro, e os elementos out-of-band e hx-partial vêm em seguida, na ordem do documento. Recorra a hx-swap-oob quando estiver trocando um elemento por uma cópia atualizada do mesmo elemento, e a hx-partial quando uma única resposta precisar atualizar vários lugares, já que cada partial declara seu próprio hx-target e hx-swap em vez de depender de atributos espalhados pela marcação.

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.