Lazy loading modules in Svelte

How to import your component on demand

Tom • Svelte & SvelteKit, Coding Guides, Learning •

Dynamic import in Svelte

When coding in Svelte, you may have the requirement to only load components on demand, when they are actually needed. React has solved this problem quite masterfully with its “Suspense”-component. If you want to achieve something similar in Svelte, I have good news for you.

Svelte "Suspense" without suspense

Basically what’s required is to use Svelte’s built-in mechanism to await async function calls in the component itself. The following simple example shows what I mean by that.

<script>
  // Dummy function, just so that we
  // have some async stuff.
	async function getData() {
		await new Promise(res => setTimeout(res, 1000));
		return { key: "value" };
	}
</script>

{#await getData()}
	<p>Fetching data...</p>
{:then res}
	<p>Accessing data: "key": "{res.key}"</p>
{:catch error}
	<p style="color: red">{error.message}</p>
{/await}

As you can see, we simply await the function’s return value and then use it in our UI. The same technique can be used for importing whole modules.

Dynamic import with Svelte

Using the dynamic “import”, available in ES6, we can write a simple lazy component import.

//
// Live demo:
// https://svelte.dev/repl/b7551180977d4e738b07d428f3172d5e?version=3.46.4
//

//
// App.svelte
// 

<h1>App title</h1>

{#await import("./Content.svelte") then Module}
	<Module.default subtitle="Subtilte as prop" />
{/await}

//
// Content.svelte
//

<script>
	export let subtitle;
</script>

<article>
  <h2>
		Content title
	</h2>
	<h3>
		{subtitle}
	</h3>
	<p>
		Content body.
	</p>
</article>

Building on this knowledge, we are now able to lazy load any component. A practical example I’m actually using for spikze.club is implementation of a single “Icon”-component that loads the specific icon on demand.

<!--
  Source: https://carbon-icons-svelte.onrender.com/
-->
<script lang="ts">
  // Using a link so that 'clsx' can eliminate
  // all other variant compared to an enum, which
  // should lead to a smaller size at runtime.
  export let variant: "internal-link" | "external-link" | "section-link" | "close" | "add-circle";
  let cn: string = undefined;
  export { cn as class };
</script>

{#if variant === "external-link"}
  {#await import("carbon-icons-svelte/lib/ArrowUpRight20") then Icon}
    <Icon.default class={cn} />
  {/await}
{/if}

{#if variant === "internal-link"}
  {#await import("carbon-icons-svelte/lib/ArrowRight20") then Icon}
    <Icon.default class={cn} />
  {/await}
{/if}

{#if variant === "section-link"}
  {#await import("carbon-icons-svelte/lib/ArrowDown20") then Icon}
    <Icon.default class={cn} />
  {/await}
{/if}

{#if variant === "close"}
  {#await import("carbon-icons-svelte/lib/Close20") then Icon}
    <Icon.default class={cn} />
  {/await}
{/if}

{#if variant === "add-circle"}
  {#await import("carbon-icons-svelte/lib/AddAlt20") then Icon}
    <Icon.default class={cn} />
  {/await}
{/if}

Wrap up

You’ve seen how to use the dynamic import from ES6 to simply import any data on demand. As this applies to both your components as well as third party libraries, there’s nothing holding you back from scaling your next Svelte app.

Actions

Feedback

Suggested Posts