How to Create a Summer Road Trip Mapping App with Gatsby and Leaflet
Summer is the perfect time to hit the open road and explore new destinations. But planning a multi-stop road trip can get overwhelming trying to visualize the route and all the places you want to visit. That‘s where an interactive mapping app can help!
In this step-by-step tutorial, we‘ll learn how to combine the powers of Gatsby and Leaflet to build a web app that showcases a summer road trip route. You‘ll be able to plot the path between stops, add markers for each location, and create custom popups with images and information about what you‘ll do at each stop.
By the end, you‘ll have a working prototype you can adapt for your own summer adventure or mapping project. Let‘s get mapping!
Prerequisites
Before we jump into the code, make sure you have the following set up:
You‘ll also need the Gatsby CLI to create a new project. Install it globally with:
npm install -g gatsby-cli
Setting up a new Gatsby project
With the setup out of the way, let‘s scaffold a new Gatsby app. In your terminal, navigate to the directory where you want to create the project and run:
gatsby new summer-road-trip-map
This will generate a new directory called summer-road-trip-map
with the basic file structure for a Gatsby app. Move into the directory and start the local development server:
cd summer-road-trip-map
gatsby develop
In your browser, navigate to http://localhost:8000
and you should see the default Gatsby starter page.
Importing Leaflet
Leaflet is a lightweight open-source library for building interactive maps. To use it in our Gatsby app, we first need to install the necessary dependencies.
Stop the development server (ctrl + c) and run:
npm install leaflet gatsby-plugin-react-leaflet
leaflet
is the core library, while gatsby-plugin-react-leaflet
makes it easier to integrate with Gatsby‘s build process.
Next, add the Leaflet CSS file to gatsby-browser.js
in the root of your project:
import "./node_modules/leaflet/dist/leaflet.css"
And finally, to tell Gatsby to use the Leaflet plugin, add it to the plugins array in gatsby-config.js
:
module.exports = {
plugins: [
‘gatsby-plugin-react-leaflet‘
]
}
Now we‘re ready to start mapping!
Preparing the map data
Before we can draw our route, we need some data to tell Leaflet what points to map. Create a new file at src/data/locations.js
and add the following:
export const locations = [
{
id: 1,
name: "Yosemite Valley",
coordinates: [37.865101, -119.538329],
description: "Explore granite cliffs, waterfalls, and sequoia groves.",
image: "https://images.unsplash.com/photo-1589810635657-232948472d98?w=600&h=400&fit=crop"
},
{
id: 2,
name: "Tuolumne Meadows",
coordinates: [37.873779, -119.358336],
description: "Enjoy subalpine meadows, starry skies and rugged peaks.",
image: "https://images.unsplash.com/photo-1426604966848-d7adac402bff?w=600&h=400&fit=crop"
},
{
id: 3,
name: "Death Valley",
coordinates: [36.507389, -117.079408],
description: "Discover exotic landscapes in this below-sea-level basin.",
image: "https://images.unsplash.com/photo-1545165375-b61286d1a2a6?w=600&h=400&fit=crop"
},
{
id: 4,
name: "Ancient Bristlecone Pine Forest",
coordinates: [37.566112, -118.172280],
description: "Gaze at the world‘s oldest trees, some nearly 5000 years old.",
image: "https://images.unsplash.com/photo-1617328587655-c3ed75e29b4a?w=600&h=400&fit=crop"
},
{
id: 5,
name: "Alabama Hills",
coordinates: [36.606941, -118.113755],
description: "Camp and sightsee amongst rusty-hued granite boulders.",
image: "https://images.unsplash.com/photo-1600521465632-e1de2bbc19da?w=600&h=400&fit=crop"
},
{
id: 6,
name: "Sequoia & Kings Canyon",
coordinates: [36.787010, -118.775558],
description: "Marvel at the largest trees in the world.",
image: "https://images.unsplash.com/photo-1585077145761-0aa2754f1e4e?w=600&h=400&fit=crop"
}
]
This represents the stops on a hypothetical road trip through California‘s Sierra Nevada mountains. Each location has:
- A unique
id
name
of the stopcoordinates
in [latitude, longitude] format- Short
description
of what you can do there image
URL to show in the popup (I‘m using landscape images from Unsplash)
Feel free to customize this data for your own trip. Just make sure to keep the same object shape for each location.
Rendering the map
With our location data ready, let‘s plot it on a map! Create a new file at src/pages/index.js
and add this code:
import * as React from ‘react‘
import { MapContainer, TileLayer } from ‘react-leaflet‘
import { locations } from ‘../data/locations‘
import Markers from ‘../components/Markers‘
import Route from ‘../components/Route‘
const MAPBOX_API_KEY = ‘YOUR MAPBOX API KEY‘;
const MAPBOX_USERID = ‘YOUR MAPBOX USERNAME‘;
const MAPBOX_STYLEID = ‘YOUR MAPBOX STYLE ID‘;
const IndexPage = () => {
return (
<div className="container">
<MapContainer
center={[37.8283, -119.5795]}
zoom={6}
scrollWheelZoom={false}
>
<TileLayer
url={`https://api.mapbox.com/styles/v1/${MAPBOX_USERID}/${MAPBOX_STYLEID}/tiles/256/{z}/{x}/{y}@2x?access_token=${MAPBOX_API_KEY}`}
attribution="Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>"
/>
<Markers locations={locations} />
<Route locations={locations} />
</MapContainer>
</div>
)
}
export default IndexPage;
Let‘s break this down:
-
First we import the
locations
data and theMarkers
andRoute
components we‘ll create next. -
For the base map layer, I‘m using a custom Mapbox tile set. For this you‘ll need a free Mapbox account. Replace
MAPBOX_API_KEY
,MAPBOX_USERID
, andMAPBOX_STYLEID
with your own credentials. -
The
MapContainer
component initializes the map, centered over the Sierra Nevada mountains at a zoom level suitable for our route. InsideMapContainer
we have:TileLayer
loads the base map tiles from MapboxMarkers
will render a marker at each stopRoute
will draw lines connecting the stops
Let‘s implement those two components now. First create a new file at src/components/Markers.js
:
import * as React from ‘react‘
import { Marker, Popup } from ‘react-leaflet‘;
import { divIcon } from ‘leaflet‘;
import ‘leaflet/dist/leaflet.css‘;
const customMarkerIcon = divIcon({
className: ‘marker‘,
iconSize: [30, 30],
iconAnchor: [15, 30],
popupAnchor: [0, -20]
});
const Markers = ({ locations }) => {
return (
<>
{locations.map(location => (
<Marker
key={location.id}
position={location.coordinates}
icon={customMarkerIcon}
>
<Popup>
<div className="popup-container">
<img src={location.image} alt={location.name} />
<div className="popup-text">
<h2>{location.name}</h2>
<p>{location.description}</p>
</div>
</div>
</Popup>
</Marker>
))}
</>
);
};
export default Markers;
For each location, this component renders a Marker
at the given coordinates
. The Popup
contains the location name, description, and image.
I‘m also using a custom divIcon
for the marker symbols instead of Leaflet‘s default.
Now let‘s add the route lines in src/components/Route.js
:
import { useEffect } from ‘react‘;
import L from ‘leaflet‘;
import ‘leaflet-routing-machine‘;
import ‘leaflet-routing-machine/dist/leaflet-routing-machine.css‘;
const Route = ({ locations = [], map }) => {
useEffect(() => {
if (!map) return;
const routingControl = L.Routing.control({
waypoints: locations.map((loc) => L.latLng(loc.coordinates)),
lineOptions: {
styles: [
{
color: ‘black‘,
opacity: 0.5,
weight: 4
}
]
},
addWaypoints: false,
draggableWaypoints: false,
fitSelectedRoutes: true,
showAlternatives: false,
}).addTo(map.leafletElement);
return () => map.leafletElement.removeControl(routingControl);
}, [map, locations]);
return null;
};
export default Route;
This component uses the Leaflet Routing Machine plugin to calculate and draw the route lines based on the location coordinates.
The useEffect
hook adds the route to the map when the component mounts and removes it on unmount. This ensures the route updates if the locations or map changes.
Styling with CSS
To polish off the design, add the following CSS to src/pages/index.css
:
.container {
height: 100vh;
}
.leaflet-container {
width: 100%;
height: 100%;
}
.marker {
background-color: #fff;
border: 2px solid #6fce72;
border-radius: 50%;
cursor: pointer;
}
.popup-container {
width: 300px;
text-align: center;
}
.popup-container img {
width: 100%;
height: auto;
margin-bottom: 0.5rem;
}
.popup-text h2 {
font-size: 1.2rem;
margin-bottom: 0.5rem;
}
.popup-text p {
font-size: 1rem;
margin-bottom: 0;
}
This styles the map to fill the window, centers the popup content, and makes the custom markers white circles with a green border.
And with that, our Summer Road Trip Mapping App is complete! To see it in action, start the development server again with gatsby develop
and open http://localhost:8000
in your browser.
Next Steps
There are many ways you could expand on this starter app:
- Add more locations and details to each stop
- Customize the map style further in Mapbox Studio
- Make the route editable by adding/removing stops
- Use Gatsby‘s data layer to pull location content from Markdown files or a CMS
- Allow the user to click on the map to add their own stops
I hope this tutorial has shown you the possibilities of combining Gatsby and Leaflet to create interactive mapping apps. With a little creativity and the power of open-source tools, you can bring your summer road trip plans to life!
Have any mapping projects of your own to share? Let me know in the comments. Happy coding, and safe travels!