Como criar um mapa interativo do Brasil com React

Mapas interativos são uma forma eficiente de comunicar informações geográficas em sites e aplicações. Em vez de listar estados ou regiões em texto, um mapa visual permite que o usuário entenda a cobertura ou presença de um serviço de forma imediata.
Neste post, mostro como criar um mapa do Brasil com React usando a biblioteca react-simple-maps. A técnica pode ser adaptada para qualquer país ou região, desde que você tenha o arquivo de dados geográficos correspondente.
O objetivo
Criar um mapa interativo do Brasil onde estados específicos aparecem destacados. O mapa deve:
- Renderizar todos os 27 estados brasileiros
- Destacar estados selecionados com cor diferente
- Mostrar tooltip com nome do estado no hover
- Ter animação de entrada suave
A stack
- React (ou Next.js)
- react-simple-maps - biblioteca para renderizar mapas SVG
- TopoJSON - formato de dados geográficos otimizado
Instalação
pnpm add react-simple-maps
pnpm add -D @types/react-simple-maps
Obtendo o TopoJSON do Brasil
O react-simple-maps precisa de um arquivo TopoJSON ou GeoJSON com as geometrias dos estados.
Fontes recomendadas:
Baixe o arquivo e salve em public/brazil-states.json.
Estrutura do TopoJSON
O arquivo tem esta estrutura:
{
"type": "Topology",
"objects": {
"estados": {
"type": "GeometryCollection",
"geometries": [
{
"type": "Polygon",
"properties": { "nome": "São Paulo" },
"id": "SP",
"arcs": [[...]]
}
]
}
},
"arcs": [...],
"transform": {...}
}
Cada estado tem:
id: sigla do estado (SP, RJ, MG, etc.)properties.nome: nome completoarcs: coordenadas do polígono
Definindo os estados ativos
Crie um arquivo com as siglas dos estados que você quer destacar:
// lib/data/client-states.ts
export const activeStates: string[] = [
"SP", // São Paulo
"MG", // Minas Gerais
"RJ", // Rio de Janeiro
"PR", // Paraná
"SC", // Santa Catarina
"RS", // Rio Grande do Sul
"BA", // Bahia
"DF", // Distrito Federal
];
Componente do mapa
// components/BrazilMap.tsx
"use client";
import { useState, memo } from "react";
import {
ComposableMap,
Geographies,
Geography,
} from "react-simple-maps";
import { activeStates } from "@/lib/data/client-states";
const GEO_URL = "/brazil-states.json";
interface HoveredState {
name: string;
code: string;
}
export function BrazilMap() {
const [hovered, setHovered] = useState<HoveredState | null>(null);
return (
<div className="relative w-full max-w-lg mx-auto">
<ComposableMap
projection="geoMercator"
projectionConfig={{
center: [-54, -15],
scale: 750,
}}
style={{ width: "100%", height: "auto" }}
>
<Geographies geography={GEO_URL}>
{({ geographies }) =>
geographies.map((geo) => {
const stateCode = geo.id as string;
const isActive = activeStates.includes(stateCode);
return (
<Geography
key={geo.rsmKey}
geography={geo}
fill={isActive ? "#d4a853" : "#e5e7eb"}
stroke="#fff"
strokeWidth={0.5}
style={{
default: { outline: "none" },
hover: {
fill: isActive ? "#1e3a5f" : "#d1d5db",
cursor: "pointer",
},
pressed: { outline: "none" },
}}
onMouseEnter={() => {
setHovered({
name: geo.properties.nome,
code: stateCode,
});
}}
onMouseLeave={() => setHovered(null)}
/>
);
})
}
</Geographies>
</ComposableMap>
{/* Tooltip */}
{hovered && (
<div className="absolute top-4 left-4 px-3 py-2 bg-gray-900 text-white text-sm rounded">
{hovered.name} ({hovered.code})
</div>
)}
</div>
);
}
Entendendo o código
ComposableMap
Container principal do mapa. Configurações importantes:
projection: tipo de projeção cartográfica.geoMercatorfunciona bem para o BrasilprojectionConfig.center: coordenadas do centro do mapa[longitude, latitude]projectionConfig.scale: zoom do mapa
Geographies
Carrega e processa o arquivo TopoJSON. Recebe o path do arquivo via prop geography.
Geography
Renderiza cada estado como um elemento SVG <path>. Props principais:
fill: cor de preenchimentostroke: cor da bordastyle: estilos para estadosdefault,hoverepressed
Ajustando a projeção
Se o mapa aparecer cortado ou fora de posição, ajuste center e scale:
projectionConfig={{
center: [-54, -15], // longitude, latitude do centro
scale: 750, // zoom (maior = mais perto)
}}
Para o Brasil inteiro, valores entre 700-850 para scale funcionam bem.
Adicionando animação de entrada
Use CSS para animar os estados quando aparecem:
/* globals.css */
@keyframes state-fade-in {
from { fill-opacity: 0; }
to { fill-opacity: 1; }
}
.animate-state-fade-in {
animation: state-fade-in 0.6s ease-out forwards;
fill-opacity: 0;
}
Aplique a classe nos estados ativos:
<Geography
className={isActive ? "animate-state-fade-in" : ""}
style={{
default: {
animationDelay: `${index * 80}ms`,
},
}}
// ...
/>
Isso cria um efeito de "cascata" onde cada estado aparece com um pequeno delay.
Otimização com memo
Para evitar re-renders desnecessários, extraia o Geography para um componente separado com memo:
const StateGeography = memo(function StateGeography({
geo,
isActive,
onHover,
onLeave,
}: Props) {
return (
<Geography
geography={geo}
fill={isActive ? "#d4a853" : "#e5e7eb"}
// ...
/>
);
});
Alternativas ao react-simple-maps
| Biblioteca | Uso |
|---|---|
| react-simple-maps | Mapas SVG estáticos, leve |
| Leaflet + react-leaflet | Mapas interativos com tiles, zoom/pan |
| Mapbox GL | Mapas 3D, alta performance |
| D3.js | Controle total, curva de aprendizado maior |
Para um mapa simples de estados sem necessidade de zoom/pan, react-simple-maps é a escolha mais direta.