Using the Script Tag in Svelte Files for 3rd Party Code

Summary

Currently svelte files can only have one instance-level <script> element.

So when you want to add 3rd party script tags in your svelte files, things go wrong. There are a few options. This article by Adam Tuttle covers most of the options pretty well (except the focus is for sveltekit). This How-To offers a few more options and my thoughts on them.

The Problem

So when you try to do this:

<script>
 // js logic
</script>

<p>Check out this cool 3rd party form I embedded</p>

<iframe id="iFrameResizer0" src="https://not-my-site.com/bookingform"></iframe>
<script src="https://not-my-site.com/my/3rd/party/script.js"></script>

Svelte errors out like this.

A component can only have one instance-level <script> element.

The 5 Solutions

1. Use svelte:element

<script>
  export let src = undefined
</script>


<svelte:element this="script" {src} {...$$restProps} />

Notice in the example above I am wrapping this in its own component for reuse. This is also a simplified version of what would be passed in.

2. Wrap it in something

If you wrap the script tag in another html element or a javascript block, that will work:

Wrap it in a div element

<div>
  <script async defer data-website-id="my-id" src="https://my.cloud/umami.js"></script>
</div>

Wrap it in a if block

{#if evalsToTrue}
  <script async defer data-website-id="my-id" src="https://my.cloud/umami.js"></script>
{/if}

3. Throw the script element in svelte:head

<svelte:head>
  <script async defer data-website-id="my-id" src="https://my.cloud/umami.js"></script>
</svelte:head>

4. Use onMount and append the script element to body

<script>
  import { onMount } from 'svelte'
  import { appendScripts } from '@/core/frontier'
  import scriptsHTMLString from '@settings/FooterScripts.md?raw'


  // Creates a script element and appends to <body>
  onMount(() => appendScripts(document, scriptsHTMLString))
</script>

5. Using vite to import the script as raw text and inject as html

<script>
  import SiteHead from '@/components/SiteHead.svelte'
  import FooterHtml from '@settings/footer.html?raw'
</script>

<SiteHead {...$$restProps} />
<slot />
{@html FooterHtml}

Where footer.html looks like this:

  <script async defer data-website-id="my-id" src="https://my.cloud/umami.js"></script>

Conclusion

All these have their pros and cons. I don’t have time to write on that now.

References

https://adamtuttle.codes/blog/2021/adding-3rd-party-script-tags-in-sveltekit/