---
title: "Trunk em Rust: Build e Deploy WebAssembly | Rust Brasil"
url: "https://rustlang.com.br/blog/trunk-rust-webassembly-build-deploy-2026/"
markdown_url: "https://rustlang.com.br/blog/trunk-rust-webassembly-build-deploy-2026.MD"
description: "Use Trunk em projetos Rust WebAssembly: configure index.html e Trunk.toml, rode o servidor local, otimize o build e publique os arquivos estáticos."
date: "2026-09-03"
author: "Equipe Rust Brasil"
---

# Trunk em Rust: Build e Deploy WebAssembly | Rust Brasil

Use Trunk em projetos Rust WebAssembly: configure index.html e Trunk.toml, rode o servidor local, otimize o build e publique os arquivos estáticos.


**Trunk é uma ferramenta de build e servidor de desenvolvimento para aplicações frontend feitas em Rust e compiladas para WebAssembly.** Você aponta o Trunk para um `index.html`, marca o binário ou biblioteca Rust com `data-trunk`, executa `trunk serve` durante o desenvolvimento e usa `trunk build --release` para gerar arquivos estáticos na pasta `dist/`. Esse fluxo é especialmente comum com Yew, mas também funciona em projetos client-side que usam `wasm-bindgen` sem depender de um framework específico.

A configuração mínima cabe em poucos comandos:

```bash
rustup target add wasm32-unknown-unknown
cargo install trunk --locked
trunk serve --open
```

Em produção, a saída é HTML, JavaScript, WebAssembly e assets estáticos. Ela pode ser publicada em Cloudflare Pages, Netlify, GitHub Pages, Gitea Pages, S3 ou qualquer servidor capaz de entregar arquivos com os tipos MIME corretos. Antes de escolher Trunk, porém, separe duas necessidades: ele é excelente para **SPA client-side e sites estáticos com Wasm**; aplicações com SSR, server functions ou um backend Rust integrado podem se beneficiar do CLI recomendado pelo próprio framework.

## Resposta rápida: quando usar Trunk

| Cenário | Trunk é uma boa escolha? | Motivo |
|---|---:|---|
| SPA com Yew | Sim | fluxo consolidado com `index.html`, assets e reload |
| Aplicação Rust/Wasm sem framework | Sim | reduz o trabalho manual com wasm-bindgen e empacotamento |
| Protótipo frontend em Rust | Sim | servidor local e build com poucos comandos |
| Leptos com SSR e server functions | Depende | `cargo-leptos` pode integrar melhor cliente e servidor |
| Dioxus fullstack | Depende | o CLI `dx` cobre o pipeline específico do framework |
| Biblioteca Wasm publicada no npm | Geralmente não é o foco | `wasm-pack` costuma ser mais direto para empacotar uma biblioteca |
| Backend Axum tradicional | Não | use Cargo e a estratégia normal de deploy do servidor |

Se a sua entrega final é uma pasta de arquivos estáticos e o navegador executa a aplicação, Trunk merece entrar na avaliação. Para comparar frameworks antes de montar o pipeline, veja [Leptos vs Dioxus vs Yew](/blog/leptos-vs-dioxus-vs-yew-2026/) e o guia de [Rust para WebAssembly](/artigos/rust-para-webassembly/).

## Criando o projeto mínimo

Comece com um projeto binário comum:

```bash
cargo new app-wasm
cd app-wasm
rustup target add wasm32-unknown-unknown
cargo install trunk --locked
```

Para um exemplo com Yew, adicione a dependência usando o [Cargo](/ecossistema/cargo/):

```bash
cargo add yew --features csr
```

O `src/main.rs` pode conter um componente pequeno:

```rust
use yew::prelude::*;

#[function_component(App)]
fn app() -> Html {
    let clicks = use_state(|| 0_u32);

    let onclick = {
        let clicks = clicks.clone();
        Callback::from(move |_| clicks.set(*clicks + 1))
    };

    html! {
        <main>
            <h1>{ "Rust + WebAssembly com Trunk" }</h1>
            <button {onclick}>
                { format!("Cliques: {}", *clicks) }
            </button>
        </main>
    }
}

fn main() {
    yew::Renderer::<App>::new().render();
}
```

Na raiz do projeto, crie `index.html`:

```html
<!doctype html>
<html lang="pt-BR">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="Aplicação Rust WebAssembly com Trunk">
    <title>App Rust Wasm</title>
    <link data-trunk rel="rust">
  </head>
  <body>
  </body>
</html>
```

O atributo `data-trunk` informa que aquele elemento pertence ao pipeline. A linha `rel="rust"` conecta o HTML ao crate atual. Ao rodar `trunk serve`, a ferramenta compila para `wasm32-unknown-unknown`, passa pelos bindings necessários e serve a saída por HTTP.

## O papel de index.html no Trunk

Diferentemente de ferramentas que começam por um arquivo de configuração JavaScript, Trunk trata o HTML como ponto de entrada. Isso deixa visível, em um único lugar, quais folhas de estilo, imagens, ícones e artefatos Rust fazem parte da página.

Alguns padrões frequentes são:

```html
<link data-trunk rel="rust" data-wasm-opt="z">
<link data-trunk rel="css" href="styles/app.css">
<link data-trunk rel="copy-dir" href="public">
<link data-trunk rel="icon" href="public/favicon.svg">
```

Nem todo projeto precisa dessas quatro linhas. Adicione apenas o que realmente existe e valide o comportamento com a versão instalada usando:

```bash
trunk --version
trunk build --help
trunk serve --help
```

Essa verificação evita copiar flags antigas de um tutorial. O ecossistema Wasm evolui, e a ajuda do binário homologado pelo time é a referência operacional para a CI.

Também mantenha o `index.html` semanticamente completo. O Wasm não substitui metadados, acessibilidade, idioma, viewport ou uma mensagem útil quando JavaScript está desativado. Uma aplicação rápida ainda pode oferecer uma experiência ruim se o HTML inicial não explicar o que está carregando.

## Configurando Trunk.toml

Projetos pequenos podem funcionar sem configuração adicional. Quando o time precisa padronizar porta, diretório de saída e comportamento de desenvolvimento, crie `Trunk.toml` na raiz:

```toml
[build]
target = "index.html"
dist = "dist"
public_url = "/"

[serve]
address = "127.0.0.1"
port = 8080
open = false
```

Trate esse arquivo como parte do contrato de build. Em vez de cada pessoa decorar uma sequência diferente, o repositório passa a registrar onde está o HTML de entrada, onde a saída será escrita e como o servidor local deve subir.

O campo `public_url` merece atenção no deploy. Para um domínio na raiz, `/` normalmente é a escolha esperada. Para uma aplicação publicada em um subdiretório, como `https://exemplo.com/ferramenta/`, a base precisa refletir esse caminho; caso contrário, o navegador pode procurar o `.wasm`, o JavaScript e os assets na raiz errada.

Faça um teste local que reproduza o caminho final. Um build que funciona em `localhost:8080/` pode quebrar quando entra em `/app/` por causa de URLs absolutas, roteamento do frontend ou regras do provedor.

## Desenvolvimento com trunk serve

O comando principal durante o desenvolvimento é:

```bash
trunk serve
```

Para abrir o navegador automaticamente:

```bash
trunk serve --open
```

O servidor observa alterações no projeto e executa o pipeline novamente. Isso encurta o ciclo entre editar, compilar e testar, mas não elimina a necessidade de entender erros do compilador. Se o build falhar, leia a primeira causa relevante em vez de apenas reiniciar o servidor.

Uma rotina produtiva combina ferramentas:

```bash
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features
trunk build
```

`cargo test` normalmente exercita a parte nativa que pode ser testada sem navegador. Para comportamento DOM, eventos e integrações web, adicione testes adequados ao ambiente Wasm ou testes ponta a ponta. A página de [estratégias de testes em Rust](/blog/testes-rust-estrategias-boas-praticas-2026/) ajuda a separar teste unitário, integração e fluxo de usuário.

### Proxy para uma API local

Durante o desenvolvimento, a SPA frequentemente precisa chamar uma API em outra porta. Trunk oferece mecanismos de proxy que podem evitar configurações improvisadas de CORS no ambiente local. Como a sintaxe aceita pode variar com a versão, confira `trunk serve --help` e registre a opção escolhida no script de desenvolvimento ou no `Trunk.toml`.

Mesmo com proxy, mantenha a fronteira explícita:

- frontend: estado de interface, validação imediata e chamadas HTTP;
- backend: autenticação, autorização, persistência e regras que não podem confiar no cliente;
- contrato: tipos de request/response, erros e versionamento.

Se a API for escrita em Rust, [Serde](/ecossistema/serde/) cuida da serialização, [Axum](/ecossistema/axum/) pode atender HTTP e [utoipa com OpenAPI](/blog/utoipa-axum-openapi-swagger-ui-rust-2026/) ajuda a documentar o contrato.

## Assets, CSS e arquivos públicos

Há duas estratégias simples para arquivos estáticos:

1. declarar cada asset relevante no `index.html` com diretivas `data-trunk`;
2. copiar um diretório inteiro quando os arquivos já têm a estrutura final esperada.

A primeira opção torna dependências mais explícitas. A segunda é prática para fontes, manifestos, imagens e arquivos que não exigem transformação. Em ambos os casos, evite copiar lixo de desenvolvimento para produção. Revise `dist/` antes do deploy:

```bash
rm -rf dist
trunk build --release
find dist -maxdepth 3 -type f -printf '%p %k KB\n' | sort
```

Pergunte para cada arquivo grande: ele precisa estar no carregamento inicial? Pode ser comprimido? Pode ser carregado sob demanda? Existe duplicação? Um `.wasm` pequeno não compensa imagens enormes, fontes redundantes ou JavaScript de terceiros bloqueando a página.

Para CSS simples, o próprio pipeline pode copiar ou processar a folha declarada. Se o projeto usa Tailwind, Sass ou outra ferramenta externa, documente onde essa etapa roda e qual comando a CI executa. Não esconda uma dependência global que só existe na máquina de uma pessoa.

## Build de produção e otimização

Gere a saída otimizada com:

```bash
trunk build --release
```

O `--release` ativa o perfil de release do Cargo. Você também pode ajustar o perfil no `Cargo.toml`:

```toml
[profile.release]
opt-level = "s"
lto = true
codegen-units = 1
panic = "abort"
strip = true
```

Essas opções são um ponto de experimento, não uma receita universal. `opt-level = "s"` busca tamanho, enquanto outras aplicações podem se beneficiar de otimização diferente. LTO e menos unidades de geração de código podem reduzir ou melhorar o artefato, mas também aumentam o tempo de build. Meça tamanho compactado, tempo de carregamento, tempo de compilação e desempenho real.

A ferramenta `wasm-opt`, do projeto Binaryen, pode aplicar otimizações adicionais. Dependendo da instalação e configuração, Trunk consegue integrá-la ao pipeline. Antes de torná-la obrigatória na CI, confirme:

- a versão está fixada ou reproduzível;
- o ganho existe depois de gzip ou Brotli;
- o tempo extra de build é aceitável;
- o artefato continua funcionando nos navegadores suportados;
- a equipe sabe diagnosticar uma regressão.

O guia de [otimização de WebAssembly em Rust](/blog/rust-webassembly-2026-aplicacoes-web/) apresenta `wasm-opt` no contexto mais amplo. Para entender quais crates e funções aumentam o binário, combine a análise com [cargo-bloat](/blog/cargo-bloat-reduzir-tamanho-binario-rust-2026/) e [cargo-llvm-lines](/blog/cargo-llvm-lines-monomorfizacao-rust-2026/).

## Deploy dos arquivos estáticos

Depois do build, publique o conteúdo de `dist/`, não o código-fonte inteiro. Um pipeline mínimo pode seguir esta ordem:

```bash
rustup target add wasm32-unknown-unknown
cargo install trunk --locked
trunk build --release
test -f dist/index.html
test -n "$(find dist -name '*.wasm' -print -quit)"
```

No provedor, verifique quatro pontos.

### 1. Tipos MIME

O servidor deve entregar `.wasm` com `application/wasm`. Provedores modernos de arquivos estáticos normalmente fazem isso automaticamente, mas um Nginx ou servidor próprio mal configurado pode responder com um tipo genérico e impedir o carregamento correto.

### 2. Compressão

Brotli e gzip reduzem o volume transferido. Avalie o tamanho efetivamente baixado pelo navegador, não apenas o arquivo bruto em `dist/`. Um artefato de 1 MB no disco pode ter comportamento bem diferente após compressão, dependendo do código e dos dados embutidos.

### 3. Cache

Arquivos com hash no nome podem receber cache longo e imutável. O `index.html` deve ter uma política mais curta, porque referencia a versão atual dos assets. Se o HTML antigo ficar preso no cache enquanto o `.wasm` correspondente já foi removido, o usuário recebe uma aplicação quebrada.

### 4. Fallback de SPA

Se o roteador usa URLs como `/painel` ou `/configuracoes`, uma atualização direta nessas rotas precisa voltar para `index.html`. Configure o fallback no provedor, mas não transforme todos os erros em HTTP 200 sem critério. Assets inexistentes devem continuar sendo erros reais; fallback é para rotas do frontend.

Teste o deploy como usuário:

- abra a raiz em janela anônima;
- navegue para uma rota interna;
- atualize a rota;
- simule rede lenta;
- confira o console e a aba Network;
- valide uma versão anterior ou procedimento de rollback.

## CI reproduzível com Trunk

Evite instalar “a versão mais recente” de todas as ferramentas em cada build sem controle. Uma atualização pode mudar a saída ou quebrar a pipeline sem nenhuma alteração no repositório. Registre a versão aprovada no workflow, em um arquivo de ferramentas ou em um script versionado.

Exemplo conceitual para uma CI:

```bash
set -euo pipefail

rustup target add wasm32-unknown-unknown
cargo install trunk --version "$TRUNK_VERSION" --locked
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features
trunk build --release

test -s dist/index.html
find dist -name '*.wasm' -size +0 -print -quit | grep -q .
```

Cacheie o diretório de dependências apenas depois de entender as chaves de invalidação. Misturar toolchain, sistema operacional, `Cargo.lock` e targets incompatíveis gera caches difíceis de diagnosticar. O artigo sobre [sccache para Rust na CI](/blog/sccache-rust-cache-compilacao-ci-2026/) cobre o cache de compilação; para imagens Docker, veja [cargo-chef](/blog/cargo-chef-cache-docker-rust-2026/).

Também imponha um orçamento de tamanho com tolerância razoável. Falhar a CI por qualquer byte novo causa ruído; ignorar crescimento contínuo permite que o carregamento piore sem revisão. Uma política útil registra o tamanho atual, alerta para variações pequenas e exige justificativa quando o aumento passa de um limite definido pelo time.

## Trunk, wasm-pack, cargo-leptos e dx

Essas ferramentas não são substitutas perfeitas.

| Ferramenta | Melhor encaixe | Saída ou responsabilidade principal |
|---|---|---|
| Trunk | SPA e frontend Rust/Wasm baseado em HTML | site estático em `dist/` + servidor local |
| wasm-pack | biblioteca Rust/Wasm consumida por JavaScript/npm | pacote com bindings e metadados |
| cargo-leptos | aplicação Leptos com integração cliente/servidor | coordenação específica do framework |
| dx | aplicações Dioxus | desenvolvimento e build do ecossistema Dioxus |
| Cargo | qualquer crate Rust | compilação, dependências, testes e perfis |

Escolha pelo produto entregue. Se você desenvolve um componente Wasm para ser importado por um aplicativo TypeScript, `wasm-pack` pode ser mais natural. Se desenvolve uma SPA Yew cujo resultado é um site estático, Trunk simplifica o caminho. Se o framework combina SSR e funções no servidor, o CLI especializado entende mais do projeto do que um empacotador genérico.

## Erros comuns e como diagnosticar

### Target wasm32 não instalado

Sintoma: o compilador não encontra o target esperado.

```bash
rustup target list --installed
rustup target add wasm32-unknown-unknown
```

### Build funciona, mas a página fica vazia

Abra o console do navegador. Procure falha ao baixar `.wasm`, exceção durante inicialização, seletor DOM ausente ou panic. Ative logs úteis no desenvolvimento e reduza o caso até um componente mínimo.

### Arquivo Wasm retorna 404

Confira `public_url`, caminho do deploy e URLs geradas em `dist/index.html`. O problema frequentemente aparece quando a aplicação sai da raiz do domínio e passa para um subdiretório.

### Rota interna retorna 404 ao atualizar

Configure o fallback de SPA no provedor. Diferencie rota de aplicação de arquivo estático real para não mascarar assets ausentes.

### Build de release fica muito lento

LTO, `codegen-units = 1`, `wasm-opt` e uma árvore grande de features podem somar minutos. Use [cargo --timings e técnicas para reduzir o tempo de compilação](/blog/rust-tempo-compilacao-otimizar-build-2026/) antes de remover otimizações sem medir.

### Binário cresce após adicionar uma crate

Investigue features padrão, duplicação de dependências, genéricos monomorfizados e código incluído apenas por conveniência. Use `cargo tree`, cargo-bloat e cargo-llvm-lines. O guia de [cargo tree](/blog/cargo-tree-grafo-dependencias-rust-2026/) ajuda a localizar duplicações e features ativadas.

## Projeto de portfólio com Trunk

Um bom projeto não deve mostrar apenas um contador. Crie um painel local-first para acompanhar tarefas técnicas, com estes requisitos:

- interface Yew compilada para Wasm;
- armazenamento local e importação/exportação JSON;
- filtro, ordenação e atalhos de teclado;
- tratamento de erros visível;
- testes da lógica independente do DOM;
- build de release com orçamento de tamanho;
- CI com fmt, Clippy, testes e Trunk;
- deploy estático com rota interna funcionando após refresh;
- README explicando arquitetura e trade-offs.

Depois, adicione uma API opcional em Axum para sincronização. Essa separação mostra que você entende a fronteira de confiança: o navegador pode validar a experiência, mas o backend continua responsável por autorização e persistência compartilhada.

Em [vagas Rust](/vagas/) e entrevistas, o valor não está em dizer “usei WebAssembly”. Explique por que escolheu Wasm, qual era a alternativa em JavaScript, como mediu o tamanho do bundle, como tratou cache, como reproduziu o build e quando **não** usaria Rust no frontend. Veja também os [projetos práticos para portfólio Rust](/carreira/projetos-praticos-rust/) e a trilha de [carreira em Rust](/blog/carreira-rust-2026/).

## Checklist antes de publicar

- [ ] `rustup target add wasm32-unknown-unknown` documentado;
- [ ] versão do Trunk controlada na CI;
- [ ] `trunk build --release` executado do zero;
- [ ] `dist/index.html` e pelo menos um `.wasm` não vazio;
- [ ] `public_url` compatível com raiz ou subdiretório;
- [ ] assets grandes revisados;
- [ ] gzip ou Brotli verificado no deploy;
- [ ] cache longo apenas para assets versionados;
- [ ] fallback de SPA testado em rota interna;
- [ ] rollback documentado;
- [ ] console do navegador sem erros;
- [ ] acessibilidade básica e estado de carregamento revisados.

## Perguntas frequentes sobre Trunk

### O que é Trunk no ecossistema Rust?

Trunk é um build tool e servidor de desenvolvimento para aplicações web em Rust/WebAssembly. Ele usa o `index.html` como entrada, coordena a compilação do crate, processa assets declarados e produz uma pasta estática pronta para hospedagem. Ele complementa o [wasm-bindgen](/ecossistema/wasm-bindgen/), que cuida da interoperabilidade entre Rust/Wasm e JavaScript.

### Como instalar e executar Trunk?

Instale o target com `rustup target add wasm32-unknown-unknown`, instale a ferramenta com `cargo install trunk --locked` e rode `trunk serve` na raiz que contém `index.html`. Em equipe, fixe uma versão na CI e confira a ajuda do binário instalado para evitar opções obsoletas.

### Trunk serve apenas para projetos Yew?

Não. Yew é o encaixe mais conhecido, mas Trunk pode orquestrar projetos client-side Rust/Wasm sem Yew. Para Leptos ou Dioxus com recursos fullstack, compare o pipeline com `cargo-leptos` ou `dx`, pois essas ferramentas conhecem as necessidades específicas dos frameworks.

### Como gerar um build de produção com Trunk?

Execute `trunk build --release`, publique o conteúdo de `dist/` e valide MIME, compressão, cache, URL-base e fallback de rotas. Não abra apenas `dist/index.html` com `file://`: sirva os arquivos por HTTP para reproduzir o comportamento do navegador em produção.

### Trunk substitui wasm-bindgen e wasm-opt?

Não. Trunk coordena etapas. `wasm-bindgen` continua sendo a ponte de interoperabilidade usada no ecossistema Rust/Wasm, enquanto `wasm-opt` é uma otimização adicional que pode reduzir o artefato em certos projetos. Mantenha qualquer etapa extra somente se o ganho for medido e o build continuar reproduzível.

## Conclusão

Trunk reduz a distância entre um crate Rust e um frontend WebAssembly publicável: um `index.html` explícito, `trunk serve` para o ciclo local e `trunk build --release` para gerar a saída estática. A ferramenta funciona melhor quando o produto é realmente client-side e o time quer um pipeline simples, versionado e fácil de reproduzir.

O erro é tratar a escolha como moda. Compare Trunk com o CLI específico do framework, meça o artefato comprimido, teste rotas e cache no ambiente real e mantenha a fronteira com o backend clara. Assim, Rust no navegador deixa de ser uma demonstração técnica e vira uma aplicação que pode ser construída, operada e explicada profissionalmente.
