In this article we explore how to add a simple map with a location marker to a webpage using free and open-source tools. No API key will be required for this map.
We'll be using:
OpenLayers - a JavaScript library that helps you display maps and add features like markers.
OpenStreetMap (OSM) - an open source project providing free maps.
Together, OpenLayers and OpenStreetMap offer a fully open-source alternative to commercial mapping platforms like Google Maps.
✅ This example is built with React, but the approach can be easily adapted to work with other frontend frameworks such as Vue, Angular, or plain JavaScript.
Here is a link to a demo that shows how the working map appears on a webpage.
1. Create a New React Project (or Use an Existing One)
If you’d like to try adding the map to a webpage, the easiest way to get started is by using a Vite template:
npm create vite@latest ol-react-app -- --template react-ts
Alternatively, you can simply install the ol
(OpenLayers) library in your existing React project and build from there.
2. Install OpenLayers
OpenLayers can be installed using npm:
npm install ol
If you prefer not to include the ol
package in your bundle, you can load it via CDN in the index.html
:
<script src="https://cdn.jsdelivr.net/npm/ol@v10.6.0/dist/ol.js"></script>
However, bundling it with your project is generally preferred for better control and performance.
3. Create the Map Component
Now we can initialize our map. In the src/Map.tsx
we import a Map component from ol
library and use it to create the map instance. The Map requires a few important parameters:
target
- tells OpenLayers where to place the map. It should be theid
orref
to an HTML element that will act as the map container.view
- sets the initial position of the map: where it’s centered and zoomed.layers
- the content that will be shown on the map.
We’ll use two layers:
OSMLayer
- a basic map layer using OpenStreetMap tiles (which gives us a detailed world map).vectorLayer
- a layer that will display a marker for a specific location.
In this example let's center our map on London. To do that, we’ll need London’s coordinates as longitude and latitude which we can easily find online.
Let’s take a closer look at our vectorLayer
to understand how it is created. We add this layer using the VectorLayer
from ol
library. This layer needs a source, which we provide using VectorSource
. The source contains our London location, represented as a Point feature.
We also define a style for this layer using the Style
. Here, we can pass an SVG icon, which we use as a marker for our London location on the map.
Lastly, our London coordinates are passed as the center
in the view
parameter of the Map, so the map is centered on London when it loads.
Here is how the whole set up looks like:
// src/Map.tsx
import React, { useEffect } from 'react';
import { Map, View, Feature } from 'ol';
import { Zoom } from 'ol/control';
import TileLayer from 'ol/layer/Tile.js';
import OSM from 'ol/source/OSM.js';
import VectorLayer from 'ol/layer/Vector.js';
import VectorSource from 'ol/source/Vector.js';
import { Point } from 'ol/geom';
import { fromLonLat } from 'ol/proj.js';
import { Style, Icon } from 'ol/style.js';
import reactSvg from './assets/pin.svg?url'; // Import the SVG as a URL
import type { Coordinate } from 'ol/coordinate';
const MAP = 'map' as const;
// Longitude, Latitude for London
const coordinatesLondon: [number, number] = [-0.118092, 51.509865];
// Convert lon/lat coordinate to OpenLayers default coordinate system
const coordinates: Coordinate = fromLonLat(coordinatesLondon);
const feature: Feature<Point> = new Feature({
geometry: new Point(coordinates),
});
// Layer for the location pin icon
const vectorLayer = new VectorLayer({
source: new VectorSource({
features: [feature],
}),
style: new Style({
image: new Icon({
src: reactSvg,
anchor: [0.5, 1],
}),
}),
});
// The basic map layer with OpenStreetMap
const OSMLayer = new TileLayer({
source: new OSM(),
});
const MapElement: React.FC = () => {
useEffect(() => {
// Initialize the map when the component mounts
// to make sure the element with id 'map' is in the DOM
const map = new Map({
target: MAP, // id of the map container
view: new View({
center: coordinates,
zoom: 12,
}),
// layers order matters for the vectorLayer to be on top
layers: [OSMLayer, vectorLayer],
controls: [new Zoom()],
});
return () => {
// Cleanup the map when the component unmounts
map.setTarget(undefined);
};
}, []);
return (
<div className="mapContainer">
<div id={MAP}></div> {/* Map container */}
<div className="attribution">
© <a href="https://www.openstreetmap.org/copyright">
OpenStreetMap
</a> contributors
</div>
</div>
);
};
Those are the key parts of the setup, now let’s take a quick look at map controls and attribution.
4. Custom Controls Styling
In OpenLayers, controls are UI elements that allow users to interact with the map, for example, zoom buttons or a fullscreen toggle. Controls are built-in and can be added or removed depending on what your map needs.
Controls are added to the map using the controls
parameter when creating a new Map
instance. In this example, we include only the default Zoom
control and apply a custom style to it.
The zoom control uses the CSS class name ol-zoom
, which we can target to customize its appearance.
.ol-zoom {
position: absolute;
bottom: 35px;
right: 10px;
display: flex;
flex-direction: column;
gap: 5px;
}
The zoom buttons are typically placed in the bottom-right corner of the map.
5. Attribution for the OSM Layer
The OSM layer is open source, but it requires attribution to OpenStreetMap contributors. The attribution text © OpenStreetMap contributors
is usually included as a absolutely positioned element in the bottom-right corner of the map.
✅ Also, it is important to set a width on the map container, otherwise the map won’t be visible on the page.
Now simply include your Map component in the page layout.
And that’s it! 🎉
You can find the full setup in the GitHub repo:
👉 Check out the complete code on GitHub
Top comments (0)