Integrating Google Maps with dark scheme in Svelte & Sapper
Learn how to integrate the Google Maps JavaScript API in Svelte and Sapper and how to configure it to have a dark color scheme.
When we developed the ShipBit website, we wanted to display a Google Map widget on our Contact page that should show our location and match the dark color scheme it is surrounded with. This was actually a bit tricky to implement in Svelte & Sapper without prior knowledge and experience in these technologies.
In general you can embed Google Maps via iFrame or their JavaScript API. While the iFrame version is definitely easier to use, it lacks some customization options and does not let you modify the color scheme apart from some rather hacky CSS solutions, so we went with the JS API.
First you need to create an API key and configure it according to the documentation. Once you have set this up, you can integrate the Google Maps API in your Svelte page:
<script>
import Map from '../components/Map.svelte';
let ready;
if (process.browser) {
window.initMap = () => {
ready = true;
};
}
</script>
<svelte:head>
<script
defer
async
src="https://maps.googleapis.com/maps/api/js?key=YOUR-API-KEY&callback=initMap&libraries=places">
</script>
</svelte:head>
<section>
{#if ready}
<Map />
{/if}
</section>
We asynchronously load the required scripts from a CDN and also include the Places API which we’ll later use to find the correct start location of the map. Note that the script tag includes a callback initMap
that will be triggered automatically once all required scripts are loaded. This is the tricky part because Google expects initMap
to be a global handler registered on the window
object. Sapper uses server-side rendering and has no access to any window
object in the client. That’s why we check if the Svelte component is currently being executed on the server-side or in the client and only if it’s the latter, we register the callback handler. Once this is triggered, we know that all scripts are available and that we can finally render the map.
Now take the Styled Maps: Night Mode template as base for your Map.svelte
component:
<script>
import { onMount } from 'svelte';
let container;
let map;
let service;
let infowindow;
let zoom = 15;
let center = { lat: 51.5127494, lng: 7.4829757 };
onMount(async () => {
const options = {
zoom,
center,
styles: [
// paste "Styled Maps: Night Mode" styles here
]
};
// setup the general Map with the provided options
map = new google.maps.Map(container, options);
// query the Google Places API
const request = {
query: 'ShipBit',
fields: ['name', 'geometry']
};
service = new google.maps.places.PlacesService(map);
// create a custom marker at the found place
service.findPlaceFromQuery(request, (results, status) => {
if (status === google.maps.places.PlacesServiceStatus.OK) {
for (let i = 0; i < results.length; i++) {
const place = results[i];
const marker = new google.maps.Marker({
map,
position: place.geometry.location
});
marker.addListener('click', () => {
infowindow = new google.maps.InfoWindow({
content: `Add your marker HTML content here`
});
infowindow.open(map, marker);
});
}
map.setCenter(results[0].geometry.location);
}
});
});
</script>
<div class="map" bind:this={container} />
<style lang="scss">
.map {
position: relative;
left: 0;
width: 100%;
height: 500px;
}
// this is how you can restyle certain classes in the map
:global(.gm-style .gm-style-iw) {
color: black;
font-size: 1.125rem;
}
</style>
We first setup the general map with some input parameters like the initial zoom level and also integrate the dark scheme styles copied before. Once the map is configured, we call the Places API to search for our company and center it in the map. If your desired location is not registered as Google Place or you want to set the location manually, just skip this step and use the center
attribute of the Map
component instead.
Once we located the place, we add a custom marker at its exact location that the user can click to see additional information.
We also added some styles to make the map responsive and resize correctly on different viewports.
Caveats
When reloading the page, the map sometimes complains that the initMap
callback is not registered (yet?). It seems like Svelte (or Sapper) does something different regarding the lifecycle functions compared to navigating to the page initially or moving back and forth from another page. In our case, this is not a big issue because there is no reason for users to refresh the page and if they do, they’ll probably figure it out because they saw the map at least once. If you know why this is happening, please let us know!