Trabalhar com módulos ES nativos no navegador (import/export, sem bundler) sempre esbarrou num problema: o navegador só entende caminhos relativos ou absolutos, então um import como import { z } from 'lodash-es' simplesmente não funciona sem um empacotador para reescrever esse caminho. Os import maps resolvem isso deixando o próprio HTML dizer ao navegador para onde cada nome de módulo deve apontar, através de um <script type="importmap">.
O problema que eles resolvem
Sem import map, todo import em um módulo ES precisa ser um caminho real:
import { formatarData } from './utils/data.js';
import confetti from 'https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.2/+esm';
Usar um nome "limpo" como em Node (import confetti from 'canvas-confetti') gera o erro Uncaught TypeError: Failed to resolve module specifier, porque o navegador não sabe o que fazer com um especificador que não é URL nem caminho relativo.
Como funciona o import map
Um <script type="importmap"> declara um objeto JSON com a chave imports, mapeando o nome usado no código para a URL real:
<script type="importmap">
{
"imports": {
"canvas-confetti": "https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.2/+esm",
"utils/": "/js/utils/"
}
}
</script>
<script type="module">
import confetti from "canvas-confetti";
import { formatarData } from "utils/data.js";
confetti();
</script>
O navegador consulta o mapa antes de resolver qualquer import, então o código pode usar nomes curtos como faria com um gerenciador de pacotes, sem precisar de build step nenhum.
Mapeando prefixos de pasta
Chaves terminadas em / funcionam como prefixo, redirecionando um diretório inteiro para outro caminho — útil para trocar a origem de vários módulos de uma vez:
<script type="importmap">
{
"imports": {
"components/": "/dist/components/",
"components/botao.js": "/dist/components/botao-v2.js"
}
}
</script>
A entrada mais específica (components/botao.js) tem prioridade sobre o prefixo genérico (components/) — assim é possível redirecionar a maioria dos módulos de uma pasta e ainda abrir uma exceção pontual.
Escopos: mapas diferentes por parte da árvore
A chave scopes permite que um mesmo nome resolva para versões diferentes dependendo de qual módulo está fazendo o import — útil quando duas bibliotecas dependem de versões distintas de uma terceira:
<script type="importmap">
{
"imports": {
"diretiva": "/js/diretiva-v2.js"
},
"scopes": {
"/js/legado/": {
"diretiva": "/js/diretiva-v1.js"
}
}
}
</script>
Qualquer módulo carregado a partir de /js/legado/ que importar "diretiva" recebe a v1, enquanto o resto da aplicação recebe a v2 — sem precisar renomear nada no código.
Verificando suporte em runtime
if (HTMLScriptElement.supports?.('importmap')) {
// seguro usar import maps
} else {
// carregar um polyfill (es-module-shims, por exemplo)
}
Quando usar
- Protótipos e projetos pequenos que querem usar módulos ES direto do navegador, sem bundler.
- Apontar nomes de pacotes para uma CDN (jsDelivr, esm.sh, unpkg), como se fosse um
node_modulesdeclarado no HTML. - Trocar a implementação de um módulo por ambiente (produção vs. desenvolvimento) só editando o mapa.
- Resolver conflitos de versão entre dependências diferentes usando
scopes.
Cuidados
Só pode existir um <script type="importmap"> por documento (múltiplos mapas exigem suporte à extensão "multiple import maps", ainda não universal), e ele precisa vir antes de qualquer <script type="module"> que dependa dele, já que o navegador aplica o mapa na ordem em que processa o HTML. Import maps não substituem um bundler em projetos grandes — não há tree-shaking, minificação ou empacotamento, apenas redirecionamento de especificadores.
Compatibilidade
Suportado nativamente em Chrome, Edge e Firefox, e no Safari a partir da versão 16.4 — cobertura ampla em navegadores modernos desde 2023. Para suportar navegadores mais antigos, o polyfill es-module-shims resolve o mesmo comportamento via JavaScript.
Conclusão
Import maps trazem para o navegador algo que só existia em ferramentas de build: a possibilidade de importar módulos por nome, não por caminho. Não eliminam a necessidade de um bundler em aplicações grandes, mas para módulos ES simples e protótipos, tiram do meio uma etapa de build inteira.
0 Comentário
Deixe seu Comentário!
Dúvida, sugestão ou só um "valeu!" — tudo é bem-vindo por aqui.