Estamos entusiasmados em anunciar um novo App HTMX no Deco Hub. O HTMX tem ganhado popularidade por sua capacidade de criar aplicações web dinâmicas sem depender fortemente do JavaScript. Aproveitando os atributos HTML, o HTMX simplifica o processo de desenvolvimento, facilitando a construção de interfaces de usuário interativas.
No deco.cx, pretendemos apoiar todos os principais frameworks, incluindo Angular, Next.JS, SvelteKit e outros. No entanto, começamos com o HTMX porque vemos um grande potencial em simplificar tanto o desenvolvimento quanto a produção de páginas web simples. O HTMX se destaca em cenários onde os desenvolvedores querem evitar as complexidades dos processos de build, substituição de módulos a quente (HMR) e outras ferramentas. Com o HTMX, o que você escreve em HTML é exatamente o que você vê tanto em ambientes de desenvolvimento quanto de produção.
Combinando o HTMX com as forças do HTML5 e CSS3, os desenvolvedores podem oferecer boas experiências de usuário e desempenho de primeira classe sem o peso de baixar, analisar e compilar bibliotecas pesadas de JavaScript. Além disso, usar o HTMX pode resultar em aplicações web mais robustas. Testar JavaScript em todos os dispositivos e navegadores (Safari, navegador Samsung, etc.) é demorado e caro. Ter sua UI renderizada no servidor economiza incontáveis horas de depuração em diferentes ambientes, reduzindo, em última análise, as despesas com Sentry.
Se o HTMX oferece tantas vantagens, por que não é o padrão para o desenvolvimento de aplicações web? O principal desafio com o HTMX está na necessidade de criar rotas para cada estado da UI, o que pode complicar o processo de desenvolvimento. Cada elemento interativo ou atualização dinâmica frequentemente requer uma rota correspondente no servidor, levando a uma proliferação de endpoints e aumento da complexidade de manutenção.
Além disso, a web é composta principalmente por servidores centralizados, e computar novos estados da UI em um servidor centralizado pode penalizar os usuários periféricos devido a problemas de latência. Isso não acontece com o deco.cx, onde nossa infraestrutura edge-first distribui o código globalmente. Isso garante que computar novos estados da UI seja muito mais barato e rápido, graças à nossa baixa latência e infraestrutura distribuída globalmente.
Para nós, no deco.cx, a parte mais difícil de usar o HTMX é ter que criar uma rota para cada estado da UI. Para resolver esse desafio, desenvolvemos um novo hook chamado useSection
. Este hook cria automaticamente rotas para renderizar seus estados da UI sem exigir que os desenvolvedores lidem manualmente com roteamento.
Para demonstrar o poder e a simplicidade do hook useSection
, vamos explorar um exemplo construindo um componente de contador.
useSection
no deco.cxNeste guia, vamos construir um componente de contador simples usando HTMX e o hook useSection
no deco.cx.
Primeiro, vejamos o código usual do Preact para este componente usando o hook useState
:
import { useState } from "preact/hooks";
export default function Section() {
const [count, setCount] = useState(0);
return (
<div class="container h-screen flex items-center justify-center gap-4">
<button
class="btn btn-sm btn-circle btn-outline no-animation"
onClick={() => setCount(count - 1)}
>
<span>-</span>
</button>
<span>{count}</span>
<button
class="btn btn-sm btn-circle btn-outline no-animation"
onClick={() => setCount(count + 1)}
>
<span>+</span>
</button>
</div>
);
}
Para refatorar este componente para HTMX, seguimos três regras simples:
useState
e useEffect
, são removidos.useState
são colocadas nas props do componente.onClick
e onChange
, são removidos.Aplicando as regras 1 e 2 ao componente Section, movemos a variável count
para as props do componente, deixando-nos com:
export default function Section({ count }: { count: number }) {
return (
<div class="container h-screen flex items-center justify-center gap-4">
<button
class="btn btn-sm btn-circle btn-outline no-animation"
onClick={() => setCount(count - 1)}
>
<span>-</span>
</button>
<span>{count}</span>
<button
class="btn btn-sm btn-circle btn-outline no-animation"
onClick={() => setCount(count + 1)}
>
<span>+</span>
</button>
</div>
);
}
Note que não precisamos mais da importação do useState
. Para aplicar a regra número 3, precisamos remover o manipulador onClick
, mas como manter a interatividade? É aí que o hook useSection
é útil.
useSection
Para implementar a funcionalidade onClick
, começamos importando useSection
de deco/hooks/useSection.ts
. Este hook permite criar um link para a instância atual da seção e substituir quaisquer props que esta seção recebe. Refatorando este componente, obtemos:
import { useSection } from "deco/hooks/useSection.ts";
export default function Section({ count = 0 }:{ count: number }) {
return (
<div class="container h-screen flex items-center justify-center gap-4">
<button
hx-get={useSection({ props: { count: count - 1 } })}
hx-target="closest section"
hx-swap="outerHTML"
class="btn btn-sm btn-circle btn-outline no-animation"
>
<span>-</span>
</button>
<span>{count}</span>
<button
hx-get={useSection({ props: { count: count + 1 } })}
hx-target="closest section"
hx-swap="outerHTML"
class="btn btn-sm btn-circle btn-outline no-animation"
>
<span>+</span>
</button>
</div>
);
}
Vamos dissecar cada parte começando pelo atributo hx-get
. useSection({ props: { count: count - 1 }})
cria um link para a instância atual da seção, mas substitui a prop count
. Isso significa que, se tivéssemos outras props, o novo valor de count
seria mesclado com os outros valores de prop, e um link para esta seção seria retornado. Isso é benéfico, pois o desenvolvedor pode agora substituir algumas props inseridas pelo CMS deco.cx.
A combinação hx-target
e hx-swap
é útil quando você quer substituir toda a seção no deco.cx, já que as seções são renderizadas sob um elemento <section/>
.
Para conexões 3G, onde realizar uma solicitação para aumentar/diminuir o contador pode ser lento, precisamos adicionar um estado de carregamento. O HTMX fornece a classe indicadora htmx-request
para este propósito. Quando o HTMX está realizando uma solicitação, a classe htmx-request
é adicionada ao DOM. Com o TailwindCSS v3, podemos criar regras específicas ativadas quando esta classe está presente. Aqui está o botão atualizado:
<button
hx-target="closest section"
hx-swap="outerHTML"
hx-get={useSection({ props: { count: count - 1 } })}
class="btn btn-sm btn-circle btn-outline no-animation"
>
<span class="inline [.htmx-request_&]:hidden">-</span>
<span class="hidden [.htmx-request_&]:inline loading loading-spinner" />
</button>
A mágica está no seletor [.htmx-request_&]:
. Isso diz ao Tailwind para criar um seletor que é ativado sempre que a classe htmx-request
estiver presente em um elemento pai. Quando realizando uma solicitação, o CSS esconderá o elemento -
e exibirá um spinner. Esta abordagem aproveita as APIs nativas da Web para melhorar a experiência do usuário, mesmo em conexões lentas. Aqui está o resultado final:
Ao usar o hook useSection
, lembre-se de que as props passadas para o hook vão para a URL final. Tenha cuidado com os limites de tamanho de URL e tente passar cargas pequenas, como booleanos e IDs.
Temos uma referência de API useSection
disponível para quem quiser se aprofundar.
Além disso, fornecemos uma receita para migrar do Preact para o HTMX e vice-versa, detalhando padrões de design comuns e como alcançá-los com HTML5 e CSS3. Com essas ferramentas, os desenvolvedores podem aproveitar o poder do HTMX e a flexibilidade do deco.cx para construir aplicações web robustas e de alto desempenho.