Introdução
Aplicativos de página única (SPA) proporcionam uma melhor experiência do usuário, removendo carregamentos de página inteira, proporcionando transições suaves entre as páginas e melhorias de desempenho dependendo da escala do aplicativo, mas eles vêm com alguns custos que podem não valer a pena para alguns tipos de aplicativos. O principal custo é a complexidade adicional de um framework SPA como Angular ou Vue.
Neste post, mostrarei como usar ASP.NET e HTMX para construir aplicativos de página única sem usar JavaScript e frameworks SPA.
O que é HTMX
HTMX é uma biblioteca que nos permite acessar recursos modernos do navegador, estendendo o HTML, sem a necessidade de usar Javascript diretamente. Ele nos dá acesso a AJAX, CSS Transitions, WebSockets e muito mais, incluindo extensões.
Ele simplifica a criação de aplicativos responsivos que oferecem aos nossos usuários uma melhor experiência sem a complexidade do Javascript.
HTMX é realmente poderoso, mas neste post vou me concentrar no atributo hx-boost.
Aplicação base
Usarei um antigo Aplicativo de exemplo do Razor Pages por Damian Edwards para mostrar como é fácil fazer um SPA com ASP.NET e HTMX.
Fiz algumas alterações, incluindo a atualização do .NET Core 2.0 para o .NET 7. Essas alterações estão no branch sample-base do repositório de amostra: AspNetCoreSPAHtmx.
💡 Estou usando Razor pages, mas o que mostrarei também é aplicável ao ASP.NET MVC.
Incluindo a biblioteca HTMX e configurações de links de menu
Vamos abrir o _Layout.cshtml e adicionar a referência ao HTMX no final da tag <body>:
| |
HTMX tem um atributo hx-boost que nos permite transformar nosso aplicativo de várias páginas em um aplicativo de página única (SPA) com apenas alguns ajustes, fazendo com que as tags <a> e <form> usem solicitações AJAX em vez de recarregamentos de página.
Ele funciona em todos os filhos do elemento ao qual é aplicado, então vamos incluí-lo na tag <nav> para que todos os links do menu usem AJAX:
<nav class="navbar navbar-inverse navbar-fixed-top" hx-boost="true" hx-target="#main-content">
Por padrão, hx-boost aplica a resposta AJAX à tag <body>, então precisamos especificar o id do destino (neste exemplo, #main-content) no atributo hx-target.
Agora, vamos criar uma div com id main-content para receber o conteúdo da resposta dentro da div body-content:
| |
ℹ️ Observe que a hora do carregamento da página inteira está no rodapé, para que possamos verificar se as solicitações são feitas com AJAX.
Removendo o layout para solicitações HTMX
Todas as solicitações feitas com HTMX terão um cabeçalho Hx-Request definido como true.
Vamos verificar isso no arquivo _ViewStart.cshtml e remover o layout da página para solicitações HTMX:
| |
Configurando os formulários
Agora, também precisamos incluir os atributos hx-boost e hx-target nas tags <form> nos arquivos Index.cshtml, New.cshtml e Edit.cshtml:
<form method="post" class="form-horizontal" hx-boost="true" hx-target="#main-content">
Agora podemos testar o aplicativo. Observe que a hora do carregamento da página no rodapé não muda:

Olhando para o console do navegador, podemos ver que a solicitação foi feita:
O conteúdo do formulário no payload da solicitação:
A resposta sem o conteúdo _Layout.cshtml:
A resposta renderizada na página:
💡 A validação de formulário ASP.NET também funcionará:
Alterando o título da página de acordo com o carregamento da página
Até agora, o título da página não está sendo alterado, porque está definido no arquivo _Layout.cshtml, que é carregado apenas no primeiro carregamento da página:
<title>@ViewData["Title"] - Razor Pages Sample + HTMX</title>
O atributo hx-boost mudará automaticamente o título da página se a resposta retornar uma tag <title>. Para fazer isso, vamos criar um novo layout para as solicitações HTMX.
Crie um _LayoutHtmxBoost.cshtml com o seguinte:
| |
Observe que retornamos uma tag <title> se o ViewData["Title"] tiver algum valor.
Agora, vamos alterar o arquivo _ViewStart.cshtml para usar o layout _LayoutHtmxBoost para solicitações HTMX:
| |
⚠️ Estamos verificando o cabeçalho
Hx-Boosted, solicitações HTMX não impulsionadas não usam este layout.
💡 HTMX automaticamente envia as páginas para o histórico do navegador:
Indicando o status de carregamento
Isso é legal, mas não temos um indicador de que a solicitação está sendo processada. Para isso, podemos usar o atributo hx-indicator, especificando o id do elemento a ser mostrado enquanto aguarda a solicitação.
O hx-indicator funciona adicionando a classe css htmx-request ao elemento especificado, que definirá a opacidade como 1. Para que isso funcione, primeiro precisamos ocultar o elemento com uma opacidade de 0, ou usar a classe css htmx-indicator que faz isso com uma transação css.
Primeiro, criei uma página parcial com um carregador de Pure CSS Loaders com o nome _LoadSpinner, e defini o id como spinner e adicionei a classe css htmx-indicator:
| |
Então, adicionei no menu:
| |
Por último, adiciono o atributo hx-indicator com o valor #spinner ao elemento <nav>:
<nav class="navbar navbar-inverse navbar-fixed-top" hx-boost="true" hx-target="#main-content" hx-indicator="#spinner">
e todos os elementos <form>:
<form method="post" class="form-horizontal" hx-boost="true" hx-target="#main-content" hx-indicator="#spinner">
💡 Para facilitar a visualização do indicador, incluí um atraso de 500 milissegundos em todos os métodos GET/POST das páginas:
1 2 3 4 5 6public async Task<IActionResult> OnGetAsync(int id) { await Task.Delay(TimeSpan.FromMilliseconds(500)); ... }
Executando o aplicativo novamente, podemos ver o indicador mostrando em todas as ações:


