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


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:

 // js logic

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

<iframe id="iFrameResizer0" src=""></iframe>
<script src=""></script>

Svelte errors out like this.

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

The 5 Solutions

1. Use svelte:element

  export let src = undefined

<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

  <script async defer data-website-id="my-id" src=""></script>

Wrap it in a if block

{#if evalsToTrue}
  <script async defer data-website-id="my-id" src=""></script>

3. Throw the script element in svelte:head

  <script async defer data-website-id="my-id" src=""></script>

4. Use onMount and append the script element to body

  import { onMount } from 'svelte'
  import { appendScripts } from '@/core/frontier'
  import scriptsHTMLString from '@settings/'

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

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

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

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

Where footer.html looks like this:

  <script async defer data-website-id="my-id" src=""></script>


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