Joachim Morken
Joachim Morken

Reputation: 341

How do I load an external JS library in Svelte/Sapper?

I have been trying to load the ace editor (https://ace.c9.io/) into my Sapper application. I had success loading it in the script tag when I loaded it in a Sapper route, but when I am trying to do the same in a Svelte component which is again rendered by a route I get the error:

ace is not defined

This is the code I have, which is working fine if it is a Sapper route:

<div id="editor"> def main():
    return sum(range(1,100))
</div>

    <script src="https://pagecdn.io/lib/ace/1.4.6/ace.js" type="text/javascript" charset="utf-8"></script>

    <script>
          var editor = ace.edit("editor");
          editor.setTheme("ace/theme/monokai");
          editor.session.setMode("ace/mode/python");
          editor.resize()
    </script>

Upvotes: 21

Views: 32928

Answers (5)

Smilefounder
Smilefounder

Reputation: 599

{@html '<script src="/js/pages/projects.js" />'}

Can be use for SvelteKit as well.

Upvotes: 1

Asaf
Asaf

Reputation: 1261

// document.js

export function loadScript(src) {
    return new Promise((resolve, reject) => {
        const script = document.createElement('script');
        script.src = src;

        document.body.appendChild(script);

        script.addEventListener('load', () => resolve(script));
        script.addEventListener('error', () => reject(script));
    });
}

And then on the svelte component

// index.svelte
<script>
    import { onMount } from 'svelte';
    import { loadScript } from './document.js';

    onMount(async () => {
      await loadScript('your-external-script.js');
      console.log('script loaded successfully!');
    };
</script>

Upvotes: 10

davidpodhola
davidpodhola

Reputation: 1065

I needed to add the Keycloak Javascript adapter. The older answer here (https://stackoverflow.com/a/61979865/2013924) did not work for me so I simply

  1. included the Keycloak script first in the template.html inside <head> like <script src="http://localhost:8080/auth/js/keycloak.js"></script>
  2. and then inside my login.svelte route used the onMount

which runs after the component is first rendered to the DOM

Works exactly as expected.

Upvotes: 4

blindfish
blindfish

Reputation: 843

I hacked together a component to load external legacy JS libraries when I first started playing with Svelte2 and just refactored it to Svelte 3.

// LibLoader.svelte

<svelte:head>
  <script bind:this={script} src={url} />
</svelte:head>

<script>
  import { onMount, createEventDispatcher } from 'svelte';

  const dispatch = createEventDispatcher();
  export let url;
  let script;

  onMount(async () => {
    script.addEventListener('load', () => {
      dispatch('loaded');
    })

    script.addEventListener('error', (event) => {
      console.error("something went wrong", event);
      dispatch('error');
    });
  });
</script>
// MyComponent.svelte

<LibLoader url="myExternalLib.js"
on:loaded="{onLoaded}" />


<script>
  import LibLoader from './LibLoader.svelte';


  function onLoaded() {
    myExternalLib.doStuff();
  }
</script>

This is not thoroughly tested and for a one-off probably doesn't warrant a separate component; but basically the approach gets round the timing issue Rich Harris mentions. These days import is obviously the better option if it is available.

Upvotes: 30

Rich Harris
Rich Harris

Reputation: 29585

The way to use an external library in Svelte is to import it. I don't know how easy it is to do that with Ace — code editors tend to be somewhat complex, with their own module systems for loading languages and themes etc — but in theory it would look something like this:

<script>
  import ace from 'ace';
  import { onMount } from 'svelte';

  let div;
  let editor;

  onMount(() => {
    // we need to use onMount because the div hasn't
    // been created by the time the init code runs
    editor = ace.edit(div);
    editor.setTheme("ace/theme/monokai");
    editor.session.setMode("ace/mode/python");
    editor.resize();

    return () => {
      // any cleanup code goes here
    };
  });
</script>

<div bind:this={div}> def main():
    return sum(range(1,100))
</div>

If importing fails, you can always do it the old-fashioned way, adding the <script src="..."> tag to your main template.html, and continuing to use ace as a global. <script src="..."> tags inside Svelte components will load asynchronously — in other words, your component's code will generally run before the external script has loaded.

Upvotes: 18

Related Questions