Search code examples
javascriptgoogle-mapssveltesveltekit

Load basic Google Map with array of coordinates in SvelteKit


I am new to web development, so also SvelteKit. I am trying to show a Google Map with markers for a list of locations. The locations come from coordinates. I have found a few similar posts here and on Reddit but I can't make any progress.

I was able to get the code from the top example here to work in a project using Vapor and Leaf but am having no success with SvelteKit.

My attempt in the default +page.svelte file that was created with the project:

<script> 

(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})
        ({key: "API_KEY", v: "weekly"});

   let map;

async function initMap() {
  const { Map } = await google.maps.importLibrary("maps");

  map = new Map(document.getElementById("map"), {
    center: { lat: -34.397, lng: 150.644 },
    zoom: 8,
  });
}

initMap();
</script>

<div id="map"></div>

<svelte:head>
    <script async src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
    <script type="module" src="./index.js"></script>
</svelte:head>

<style>
    #map {
  height: 100%;
  width: 100%;
    }
</style>

Solution

  • enter image description here

    Repo with fixes:


    Your code has three problems:

    1. Missing ./index.js

    Diagnosed by: reading error in local server console after loading page.

    Fix: remove offending <script> tag from HTML.

    2. Referencing document outside browser context.

    Diagnosed by: reading error in local server console after loading page (after fixing #1). Or inspecting error in browser dev console.

    Fix: wrap offending code in onMount(). (Alternatively use SvelteKit browser)

    3. #map element has 0px height.

    Diagnosed by: inspecting element in browser dev tools.

    Fix: adjust CSS. (Quick fix is to use px, better fix is ensure parent elements have non-zero height.)


    UPDATE: I updated the project with (SvelteKit) best practices:

    • Use @googlemaps/js-api-loader.
    • Use bind instead of id
    • Use element style props
    • Use 100% height
    • Don't commit sensitive data like API keys

    Each change was saved to individual commits: https://github.com/Leftium/kit-demos/commits/google-maps/

    The final code looks like this:

    <script lang="ts">
        import '../app.css';
        import { onMount } from 'svelte';
        import { Loader } from '@googlemaps/js-api-loader';
    
        import { PUBLIC_GOOGLEMAPS_API_KEY } from '$env/static/public';
    
        // Bindings
        let mapElement: HTMLElement;
    
        onMount(async function () {
            const loader = new Loader({
                apiKey: PUBLIC_GOOGLEMAPS_API_KEY,
                version: 'weekly'
            });
    
            const { Map } = await loader.importLibrary('maps');
    
            let map = new Map(mapElement, {
                center: { lat: -34.397, lng: 150.644 },
                zoom: 8
            });
        });
    </script>
    
    <div bind:this={mapElement} style:height={'100%'} style:width={'100%'} />