Learn how to build an interactive map app on Replit using JavaScript and APIs. Perfect for beginners exploring web development and mapping tools.

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
A simple way to build a map application in Replit is to create a small full-stack project using HTML, JavaScript, and a mapping library like Leaflet.js. Leaflet is open-source, works without needing a Google Maps API key, and is ideal for demos or small projects. You’ll set up an index.html for the map display, a script.js to handle loading and markers, and optionally a server.js (Node/Express) if you later want to send or receive coordinates. You can host everything easily on Replit’s built-in web server.
Create a new Repl using the “HTML, CSS, JS” template. This gives you index.html, script.js, and style.css already.
This file is where your map container lives. Replace the existing HTML in index.html with:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Map App</title>
<!-- Leaflet CSS -->
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css"/>
<style>
/* Set full height map */
#map {
height: 100vh;
width: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="map"></div>
<!-- Leaflet JS -->
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<!-- Your custom JS -->
<script src="script.js"></script>
</body>
</html>
This creates a full-screen map div with ID map where Leaflet will load the map.
In Replit, open script.js (it’s already there). Delete demo content (like “console.log(‘hello world’)”), then add this working example:
// Initialize map, set initial view to specific coordinates and zoom
const map = L.map('map').setView([40.7128, -74.0060], 13); // New York example
// Add the OpenStreetMap tile layer (free map tiles)
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
// Add a sample marker
const marker = L.marker([40.7128, -74.0060]).addTo(map);
marker.bindPopup("Hello from New York!").openPopup();
// Optional: Add click event to put marker where user clicks
map.on('click', function(e) {
const { lat, lng } = e.latlng;
L.marker([lat, lng]).addTo(map)
.bindPopup(`Marker at ${lat.toFixed(4)}, ${lng.toFixed(4)}`)
.openPopup();
});
This script loads the map, shows tiles from OpenStreetMap, places a marker in New York, and adds click interaction to drop pins dynamically. Replit’s live preview will display the map instantly when you click “Run.”
If you want users to save markers or pull data from a database, you can add a backend. In Replit, change your project type to Node.js (or open a new Node Repl). Then create a new file server.js:
import express from "express";
import cors from "cors";
const app = express();
app.use(cors());
app.use(express.json());
// Temporary in-memory storage
let markers = [];
// Endpoint to get all markers
app.get("/api/markers", (req, res) => {
res.json(markers);
});
// Endpoint to add a new marker
app.post("/api/markers", (req, res) => {
const { lat, lng } = req.body;
markers.push({ lat, lng });
res.json({ message: "Marker added!", markers });
});
app.listen(3000, () => console.log("Server running on port 3000"));
Then in script.js (front-end), you can use fetch() to call this backend and save or load markers dynamically. You’ll need to host both front and back ends together or use Replit’s built-in HTTP server under a single Node.js Repl for simplicity.
If you decide to switch to Google Maps JavaScript API, you’ll need an API key. In Replit:
Once you run your project, you’ll have a fully functioning, zoomable, draggable map inside Replit — hosted, shareable, and editable collaboratively. From here, you can keep expanding: adding location search, geocoding APIs, or user location detection via navigator.geolocation.
<!-- server.js -->
<script type="module">
import express from "express";
import fetch from "node-fetch";
const app = express();
app.use(express.json());
// Example: API endpoint to get nearby locations with structured data for map markers
app.get("/api/places", async (req, res) => {
const { lat, lng, radius = 1000 } = req.query;
if (!lat || !lng) return res.status(400).json({ error: "Missing lat/lng" });
try {
// Example using OpenStreetMap Nominatim public API
const response = await fetch(
`https://nominatim.openstreetmap.org/search?format=json&q=restaurant&limit=10&viewbox=${lng - 0.01},${lat + 0.01},${lng + 0.01},${lat - 0.01}`
);
const data = await response.json();
const structured = data.map((place) => ({
id: place.place\_id,
name: place.display\_name.split(",")[0],
lat: parseFloat(place.lat),
lng: parseFloat(place.lon),
type: place.type,
}));
res.json({ results: structured });
} catch (err) {
console.error(err);
res.status(500).json({ error: "Failed to fetch place data" });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
</script>
<!-- server.js -->
<script type="module">
import express from "express";
import fetch from "node-fetch";
const app = express();
app.use(express.json());
// Example: Proxy endpoint to request Mapbox Geocoding API with secret key in Replit secrets
app.get("/api/geocode", async (req, res) => {
const { address } = req.query;
if (!address) return res.status(400).json({ error: "Missing address query" });
try {
const url = \`https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(
address
)}.json?access_token=${process.env.MAPBOX_TOKEN}\`;
const response = await fetch(url);
if (!response.ok) throw new Error("Mapbox request failed");
const json = await response.json();
const firstResult = json.features[0];
if (!firstResult) return res.status(404).json({ error: "No results found" });
// Return simplified structure useful for map center or marker placement
res.json({
name: firstResult.place\_name,
coordinates: {
lng: firstResult.center[0],
lat: firstResult.center[1],
},
});
} catch (err) {
console.error("Geocode error:", err);
res.status(500).json({ error: "Failed to fetch geocode data" });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Geo API running on port ${PORT}`));
</script>
<!-- server.js -->
<script type="module">
import express from "express";
import fetch from "node-fetch";
const app = express();
app.use(express.json());
// Aggregates weather + reverse geocoding data for a given lat/lng (for a map popup)
app.get("/api/location-info", async (req, res) => {
const { lat, lng } = req.query;
if (!lat || !lng) return res.status(400).json({ error: "Missing coordinates" });
try {
const [geoRes, weatherRes] = await Promise.all([
fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}`),
fetch(`https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lng}¤t_weather=true`)
]);
const [geoData, weatherData] = await Promise.all([geoRes.json(), weatherRes.json()]);
const place = geoData.display\_name || "Unknown location";
const weather = weatherData.current\_weather;
res.json({
place,
coordinates: { lat: parseFloat(lat), lng: parseFloat(lng) },
weather: weather ? {
temp: weather.temperature,
windspeed: weather.windspeed,
condition: weather.weathercode
} : null
});
} catch (error) {
console.error("Error fetching location info:", error);
res.status(500).json({ error: "Failed to load location info" });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`📍 Location info API running on ${PORT}`));
</script>

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
Building a map application on Replit works great if you keep responsibilities simple: use a Node.js + Express backend for API keys and proxy requests, and a plain React or vanilla JavaScript frontend for map rendering using a real map library such as Leaflet or Mapbox GL JS. The biggest thing to remember is that Replit exposes everything running on the frontend to the public, so never hardcode your API keys—store them in Replit’s Secrets. Also, Replit has limited file-level persistence if you’re using SQLite, so use an external database for saving location data. You can absolutely get a small, live, collaborative map app working from a single Repl, but treat it as a lightweight deployment rather than a heavy GIS backend.
Start a new Repl using the Node.js or React template. If you’re mixing frontend and backend, create a structure like this:
replit-map-app/
├── index.js // Backend (Express server)
├── public/
│ ├── index.html // Frontend HTML page
│ ├── script.js // Map logic
│ └── style.css // Optional CSS
└── package.json
In Replit, open the Shell tab, and install needed packages:
npm install express leaflet
Go to the padlock icon ("Secrets") in the Replit sidebar and create a secret variable called MAPBOX\_TOKEN or whatever you use. You’ll read this on the server side.
index.js
const express = require('express')
const app = express()
const path = require('path')
// Load the map API key from Replit secrets
const MAPBOX_TOKEN = process.env.MAPBOX_TOKEN
// Serve static files
app.use(express.static(path.join(__dirname, 'public')))
// Simple endpoint to provide token safely
app.get('/token', (req, res) => {
res.json({ token: MAPBOX_TOKEN })
})
app.listen(3000, () => console.log('Server running on port 3000'))
This approach protects the key: it’s available in backend only, and your frontend will fetch it through your backend route.
In public/index.html, create a minimal page where Leaflet (the map library) can render the map:
<!DOCTYPE html>
<html>
<head>
<title>Replit Map App</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<style>
#map { height: 100vh; width: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script src="script.js"></script>
</body>
</html>
In public/script.js, load your secure token and initialize a Leaflet map. This file runs in the browser.
fetch('/token')
.then(res => res.json())
.then(data => {
const token = data.token
// For Mapbox tiles:
const map = L.map('map').setView([37.7749, -122.4194], 13)
L.tileLayer(`https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=${token}`, {
maxZoom: 18,
id: 'mapbox/streets-v11', // default Mapbox style
tileSize: 512,
zoomOffset: -1,
attribution: '© Mapbox © OpenStreetMap'
}).addTo(map)
// Optional: add a marker
L.marker([37.7749, -122.4194]).addTo(map)
.bindPopup('Hello from San Francisco!')
})
.catch(err => console.error(err))
This code goes entirely in script.js. You’ll see your map when your server runs.
Click the Run button at the top. Your Replit console will show:
Server running on port 3000
Then click the open browser tab (or the “Open in a new tab” link). You’ll see your map rendering. Leaflet handles all map tiles, zooming, and markers directly in your browser.
Replit auto-hosts your app once it’s running. For public sharing, click “Share” → “Publish”. Make sure your backend uses simple routes only; Replit’s free hosting doesn’t support multiple processes or load balancing. It’s perfect for lightweight maps but not for heavy geospatial analytics.
That’s the most stable, production-like way to build and run a map app on Replit — by keeping your backend tiny, your secrets properly managed, your frontend clean, and your expectations realistic about Replit’s role as a single-container app runner.
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.