Google Maps API em React e Next.js: TypeScript + @vis.gl
Tutorial completo de integração Google Maps JavaScript API com React Next.js. Aprenda geolocalização, mapas interativos, rotas dinâmicas e marcadores personalizados usando TypeScript e as melhores práticas de desenvolvimento web.
@vis.gl/react-google-maps · TypeScript · App Router · AdvancedMarker · Directions
Por que isso é importante
Google Maps React Next.js: use @vis.gl/react-google-maps com 'use client', APIProvider + Map/AdvancedMarker, key em NEXT_PUBLIC_ com restrição de referrer, e monitore billing/SKU — o crédito legado US$200 não é o modelo atual.
Leitura relacionada: curso de Maps Scrapper · curso de Next.js · curso de React · Next.js 16: o que mudou · aplicação fullstack em 90 dias.
Recursos Poderosos
Tudo que você precisa para criar aplicações de mapeamento profissionais
- 01
Geolocalização Avançada com Google Maps API
Implemente geolocalização precisa usando Google Maps JavaScript API e navegador. Tutorial completo para obter coordenadas GPS do usuário em aplicações React Next.js.
- 02
Rotas Dinâmicas e Directions API
Crie rotas interativas com Google Maps Directions API. Aprenda a renderizar caminhos, calcular distâncias e implementar navegação turn-by-turn em React.
- 03
Performance Otimizada Next.js
Carregamento assíncrono de Google Maps com Next.js. Otimizações de performance, lazy loading e melhores práticas para aplicações web rápidas.
- 04
TypeScript Google Maps Integration
Tutorial TypeScript para Google Maps API. Tipagem completa, interfaces personalizadas e desenvolvimento type-safe com React e Next.js.
- 05
Google Maps Services Completos
Integração completa usando @googlemaps/google-maps-services-js e @googlemaps/js-api-loader. Places API, Geocoding e todas as funcionalidades.
- 06
Customização e Marcadores Personalizados
Crie marcadores personalizados, estilos de mapa únicos e componentes reutilizáveis. Tutorial completo de customização para Google Maps em React.
Configuração Rápida
Siga estes passos simples para começar a usar o Google Maps em seu projeto Next.js
- 1
Instalação
Instale as bibliotecas necessárias:
terminal npm install @googlemaps/google-maps-services-js @googlemaps/js-api-loader - 2
API Key
Configure sua chave de API do Google:
.env.local NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=YOUR_API_KEY - 3
Configuração
Importe e use em seu componente:
component.tsx import { useLoadMap } from "./hooks"
Exemplos de Código
Códigos prontos para usar do tutorial completo do Google Maps API
# Instalação das dependências
npm install @googlemaps/js-api-loader
# Configuração da API Key
# Adicione no seu arquivo .env.local:
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=sua_api_key_aqui
# Tipos TypeScript (opcional)
npm install --save-dev @types/google.mapsimport { Loader } from '@googlemaps/js-api-loader';
import { useEffect, useRef } from 'react';
function MapComponent() {
const mapRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const initMap = async () => {
const loader = new Loader({
apiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY!,
version: 'weekly',
libraries: ['places']
});
const { Map } = await loader.importLibrary('maps');
const map = new Map(mapRef.current!, {
center: { lat: -23.5505, lng: -46.6333 }, // São Paulo
zoom: 12,
mapTypeId: 'roadmap'
});
};
initMap();
}, []);
return (
<div
ref={mapRef}
style={{ width: '100%', height: '400px' }}
/>
);
}
export default MapComponent;import { Loader } from '@googlemaps/js-api-loader';
import { useEffect, useRef } from 'react';
function MapWithMarkers() {
const mapRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const initMap = async () => {
const loader = new Loader({
apiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY!,
version: 'weekly'
});
const { Map } = await loader.importLibrary('maps');
const { Marker } = await loader.importLibrary('marker');
const map = new Map(mapRef.current!, {
center: { lat: -23.5505, lng: -46.6333 },
zoom: 12
});
// Marcador simples
const marker = new Marker({
position: { lat: -23.5505, lng: -46.6333 },
map: map,
title: 'São Paulo'
});
// Marcador personalizado
const customMarker = new Marker({
position: { lat: -23.5489, lng: -46.6388 },
map: map,
title: 'Marco Zero',
icon: {
url: '/custom-marker.png',
scaledSize: new google.maps.Size(40, 40)
}
});
// InfoWindow
const infoWindow = new google.maps.InfoWindow({
content: '<h3>São Paulo</h3><p>Capital do estado de São Paulo</p>'
});
marker.addListener('click', () => {
infoWindow.open(map, marker);
});
};
initMap();
}, []);
return <div ref={mapRef} style={{ width: '100%', height: '400px' }} />;
}
export default MapWithMarkers;import { Loader } from '@googlemaps/js-api-loader';
import { useEffect, useRef, useState } from 'react';
function DirectionsMap() {
const mapRef = useRef<HTMLDivElement>(null);
const [directionsService, setDirectionsService] = useState<google.maps.DirectionsService | null>(null);
const [directionsRenderer, setDirectionsRenderer] = useState<google.maps.DirectionsRenderer | null>(null);
useEffect(() => {
const initMap = async () => {
const loader = new Loader({
apiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY!,
version: 'weekly',
libraries: ['routes']
});
const { Map } = await loader.importLibrary('maps');
const { DirectionsService, DirectionsRenderer } = await loader.importLibrary('routes');
const map = new Map(mapRef.current!, {
center: { lat: -23.5505, lng: -46.6333 },
zoom: 12
});
const service = new DirectionsService();
const renderer = new DirectionsRenderer({
draggable: true,
map: map
});
setDirectionsService(service);
setDirectionsRenderer(renderer);
// Calcular rota
service.route({
origin: { lat: -23.5505, lng: -46.6333 }, // São Paulo
destination: { lat: -23.5489, lng: -46.6388 }, // Marco Zero
travelMode: google.maps.TravelMode.DRIVING
}, (result, status) => {
if (status === 'OK' && result) {
renderer.setDirections(result);
}
});
};
initMap();
}, []);
const calculateRoute = (origin: string, destination: string) => {
if (!directionsService || !directionsRenderer) return;
directionsService.route({
origin: origin,
destination: destination,
travelMode: google.maps.TravelMode.DRIVING
}, (result, status) => {
if (status === 'OK' && result) {
directionsRenderer.setDirections(result);
}
});
};
return (
<div>
<div style={{ marginBottom: '10px' }}>
<button onClick={() => calculateRoute('São Paulo, SP', 'Rio de Janeiro, RJ')}>
São Paulo → Rio de Janeiro
</button>
<button onClick={() => calculateRoute('São Paulo, SP', 'Campinas, SP')}>
São Paulo → Campinas
</button>
</div>
<div ref={mapRef} style={{ width: '100%', height: '400px' }} />
</div>
);
}
export default DirectionsMap;import { Loader } from '@googlemaps/js-api-loader';
import { useEffect, useRef, useState } from 'react';
function PlacesMap() {
const mapRef = useRef<HTMLDivElement>(null);
const searchRef = useRef<HTMLInputElement>(null);
const [map, setMap] = useState<google.maps.Map | null>(null);
useEffect(() => {
const initMap = async () => {
const loader = new Loader({
apiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY!,
version: 'weekly',
libraries: ['places']
});
const { Map } = await loader.importLibrary('maps');
const { PlacesService, Autocomplete } = await loader.importLibrary('places');
const mapInstance = new Map(mapRef.current!, {
center: { lat: -23.5505, lng: -46.6333 },
zoom: 12
});
setMap(mapInstance);
// Autocomplete
if (searchRef.current) {
const autocomplete = new Autocomplete(searchRef.current);
autocomplete.bindTo('bounds', mapInstance);
autocomplete.addListener('place_changed', () => {
const place = autocomplete.getPlace();
if (!place.geometry || !place.geometry.location) return;
mapInstance.setCenter(place.geometry.location);
mapInstance.setZoom(15);
new google.maps.Marker({
position: place.geometry.location,
map: mapInstance,
title: place.name
});
});
}
// Places Service
const service = new PlacesService(mapInstance);
// Buscar restaurantes próximos
const request = {
location: { lat: -23.5505, lng: -46.6333 },
radius: 1000,
type: 'restaurant'
};
service.nearbySearch(request, (results, status) => {
if (status === google.maps.places.PlacesServiceStatus.OK && results) {
results.slice(0, 5).forEach(place => {
if (place.geometry?.location) {
new google.maps.Marker({
position: place.geometry.location,
map: mapInstance,
title: place.name,
icon: {
url: 'https://maps.google.com/mapfiles/ms/icons/restaurant.png'
}
});
}
});
}
});
};
initMap();
}, []);
return (
<div>
<input
ref={searchRef}
type="text"
placeholder="Buscar lugares..."
style={{
width: '300px',
padding: '10px',
marginBottom: '10px',
border: '1px solid #ccc',
borderRadius: '4px'
}}
/>
<div ref={mapRef} style={{ width: '100%', height: '400px' }} />
</div>
);
}
export default PlacesMap;Tutorial Completo
Siga nosso guia passo a passo para dominar a integração do Google Maps com Next.js
~110 min total · 4 etapas
- 1
Fácil · 15 min
Configuração Inicial
Instale as dependências e configure a API key do Google Maps
- Instalação de pacotes
- Configuração de variáveis de ambiente
- Setup inicial do projeto
- 2
Médio · 30 min
Classes e Utilitários
Implemente as classes RouteGoogle e MapGoogle para gerenciar mapas e rotas
- Classe RouteGoogle
- Classe MapGoogle
- Funções utilitárias
- Conversão de dados
- 3
Médio · 25 min
Hooks Personalizados
Crie hooks React para carregar mapas e gerenciar rotas
- Hook useLoadMap
- Hook useCreateMapRoute
- Geolocalização
- Estado do formulário
- 4
Avançado · 40 min
Interface do Usuário
Desenvolva componentes React para interação com mapas
- Formulário de criação
- Autocompletar locais
- Visualização de rotas
- Marcadores personalizados
Este é um material auxiliar do bootcamp CrazyStack Typescript do DevDoido. Ele servirá como uma espécie de documentação de alguns códigos vistos durante as aulas apenas como material complementar, garanta já sua vaga no bootcamp em crazystack.com.br.
Stack 2026: @vis.gl/react-google-maps no App Router
Caminho PT atual: pacote @vis.gl/react-google-maps, componente com 'use client', key em NEXT_PUBLIC_GOOGLE_MAPS_API_KEY só com restrição por HTTP referrer ativa. Tutoriais PT antigos ainda ensinam LoadScript legado — prefira APIProvider + Map da doc vis.gl (SSR não é suportado pela Maps JS API).
'use client';
import { APIProvider, Map } from '@vis.gl/react-google-maps';
export default function MyMap() {
return (
<APIProvider apiKey={process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY!}>
<Map
mapId="SEU_MAP_ID"
defaultZoom={12}
defaultCenter={{ lat: -23.55, lng: -46.63 }}
gestureHandling="greedy"
style={{ width: '100%', height: 400 }}
/>
</APIProvider>
);
}Fonte: guia SSR & frameworks do vis.gl — o directive use client exclui o mapa do SSR no App Router.
Checklist Cloud Console: billing, APIs e restrição de key
Antes do mapa na tela — sem isso o tutorial “funciona no localhost” e estoura na produção. Ponte natural para lead-gen / curso Maps Scrapper — sem inventar scrapes.
- Ative faturamento com alerta de orçamento (Maps Platform pós-2025 usa caps/SKU — não assuma crédito US$200 legado).
- Habilite Maps JavaScript API (e Places/Geocoding só se for usar).
- Crie a key com restrição por HTTP referrer do seu domínio.
Map ID, AdvancedMarker e InfoWindow
AdvancedMarker só funciona em mapa com cloud-based styling: crie um Map ID no Cloud Console e passe em mapId no componente Map (doc vis.gl AdvancedMarker).
- InfoWindow: mantenha no máximo um aberto; feche o anterior ao clicar outro marker; conteúdo com texto/ARIA — nunca injete HTML cru de usuário.
- Geolocation: peça permissão só sob gesto do usuário; trate deny/timeout; não assuma lat/lng sem fallback (centro padrão + mensagem).
- Ponte lead-gen / curso Maps Scrapper: use Places/Geocoding só com APIs habilitadas e quota monitorada — sem inventar scrapes.
Código de rotas (Directions) — material do bootcamp
Este código TypeScript define uma classe RouteGoogle, que implementa RouteProtocol. Ela cria e gerencia rotas no Google Maps, com marcadores para início e destino, mais um marcador do veículo em movimento.
1. Importação de módulos e tipos:
import type { DirectionsResponseData } from "@googlemaps/google-maps-services-js";
import { RouteProtocol } from "./route.protocol";
import { convertDirectionsResponseToDirectionsResult } from "./mapUtils";- Importa
DirectionsResponseDatade@googlemaps/google-maps-services-js. Serve pra lidar com os dados de resposta das direções do Google Maps. - Traz a interface
RouteProtocoldo módulo localroute.protocol. Define o contrato queRouteGoogleprecisa seguir. - Importa a função
convertDirectionsResponseToDirectionsResultdemapUtils.
2. Declaração da classe RouteGoogle:
export class RouteGoogle implements RouteProtocol {
// Propriedades da classe
}A classe RouteGoogle é exportada e segue RouteProtocol.
3. Construtor:
constructor(options: {
startMarkerOptions: google.maps.MarkerOptions;
endMarkerOptions: google.maps.MarkerOptions;
carMarkerOptions: google.maps.MarkerOptions;
}) {
// Inicialização das propriedades da classe
}O construtor recebe options com três campos: startMarkerOptions, endMarkerOptions e carMarkerOptions. Eles configuram os marcadores de início, fim e carro.
4. Inicialização de marcadores e DirectionsRenderer:
this.startMarker = new google.maps.Marker(startMarkerOptions);
this.endMarker = new google.maps.Marker(endMarkerOptions);
this.carMarker = new google.maps.Marker(carMarkerOptions);
const { strokeColor } = this.startMarker.getIcon() as google.maps.Symbol;
this.directionsRenderer = new google.maps.DirectionsRenderer({
suppressMarkers: true,
polylineOptions: { strokeColor, strokeOpacity: 0.5, strokeWeight: 5 }
});
this.directionsRenderer.setMap(this.startMarker.getMap() as google.maps.Map);Aqui criamos três marcadores (startMarker, endMarker e carMarker) usando as opções passadas. DirectionsRenderer é configurado pra desenhar a rota. O estilo da linha usa a cor do ícone do marcador inicial.
Código completo da classe RouteGoogle
import type { DirectionsResponseData } from "@googlemaps/google-maps-services-js";
import { RouteProtocol } from "./route.protocol";
import { convertDirectionsResponseToDirectionsResult } from "./mapUtils";
export class RouteGoogle implements RouteProtocol {
public startMarker: google.maps.Marker;
public endMarker: google.maps.Marker;
public carMarker: google.maps.Marker;
public directionsRenderer: google.maps.DirectionsRenderer;
constructor(options: {
startMarkerOptions: google.maps.MarkerOptions;
endMarkerOptions: google.maps.MarkerOptions;
carMarkerOptions: google.maps.MarkerOptions;
}) {
const { startMarkerOptions, endMarkerOptions, carMarkerOptions } = options;
this.startMarker = new google.maps.Marker(startMarkerOptions);
this.endMarker = new google.maps.Marker(endMarkerOptions);
this.carMarker = new google.maps.Marker(carMarkerOptions);
const { strokeColor } = this.startMarker.getIcon() as google.maps.Symbol;
this.directionsRenderer = new google.maps.DirectionsRenderer({
suppressMarkers: true,
polylineOptions: { strokeColor, strokeOpacity: 0.5, strokeWeight: 5 }
});
this.directionsRenderer.setMap(this.startMarker.getMap() as google.maps.Map);
}
delete(): void {
this.startMarker.setMap(null);
this.endMarker.setMap(null);
this.carMarker.setMap(null);
this.directionsRenderer.setMap(null);
}
async calculateRoute(
directionsResponseData?: DirectionsResponseData & { request: any }
): Promise<void> {
if (directionsResponseData) {
const directionsResult = convertDirectionsResponseToDirectionsResult(
directionsResponseData
);
this.directionsRenderer.setDirections(directionsResult);
}
}
// Outros métodos da classe...
}Pronto para Começar?
Transforme suas ideias em aplicações web poderosas com mapas interativos e funcionalidades avançadas de geolocalização
- Tutorial Completo
- Acesse o tutorial detalhado com exemplos práticos
- Código Fonte
- Baixe todos os arquivos e exemplos do projeto
- CrazyStack
- Participe do bootcamp completo de TypeScript
Material auxiliar do CrazyStack Typescript do DevDoido. Garanta já sua vaga no bootcamp e domine o desenvolvimento full-stack com TypeScript.
Fontes
Revisão em agosto de 2026. Créditos, caps e SKUs do Google Maps Platform mudam (ex. ajustes de 2025+) — o free tier legado de US$200 não deve ser assumido. Restrinja API keys e monitore billing no Console.
Google Maps JavaScript API · Maps — Billing March 2025 · @vis.gl/react-google-maps.
Perguntas frequentes
- Como integrar Google Maps em React/Next.js com TypeScript?
- Use @vis.gl/react-google-maps com APIProvider e um componente client ('use client'). A Maps JavaScript API não renderiza bem em SSR puro no App Router.
- Funciona com App Router do Next.js?
- Sim: isole o mapa num Client Component, exponha a key via NEXT_PUBLIC_ só se restringida por HTTP referrer, e evite importar o mapa em Server Components.
- Quanto custa a Google Maps Platform?
- Há crédito/quota e billing por SKU; a tabela mudou (caps/SKU desde 2025+). Não assuma o crédito legado US$200. Restrinja a key e monitore o Cloud Console.
- Como adicionar markers e rotas?
- Comece com Map + AdvancedMarker (Map ID quando exigido). Directions/Places são APIs extras — habilite no console e trate billing à parte.
Continue explorando: curso de Maps Scrapper · curso de Next.js · curso de React · Next.js 16: o que mudou · aplicação fullstack em 90 dias.