npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@svelte-drama/suspense

v0.7.0

Published

Put Svelte in Suspense

Downloads

1,021

Readme

Suspense for Svelte

This is a Svelte component that implements the core idea of React's <Suspense>.

When requesting asynchronous data, a typical pattern is for a parent component to handle all fetching and then push data down to its children. This can become difficult as levels of nesting increase and add unnessecary amounts of complexity. <Suspense> instead lets its children, at an arbitrary nested depth, dictate when they are done loading.

See it in action

Installation

npm install --save @svelte-drama/suspense

suspend

Child components need to register what data they depend on. suspend returns a function to to handle orchestration between this component and its nearest parent <Suspense> component.

suspend<T>(data: Promise<T>) => Promise<T>

Wrap a promise. This returns a promise, allowing it to be used as part of a promise chain. The containing <Suspense> component will display its loading state until the promise is resolved. If the promise is rejected, it will instead show the error state.

suspend<T>(data: Readable<T>, error?: Readable<Error | undefined>) => Readable<T>

Wrap a store. <Suspense> will consider this resolved as long as data resolves to not undefined. If error is passed in, <Suspense> will display the error state as long as data is undefined and error is not.

suspend.all<T extends unknown[]>(...data) => Promise<{
  [P in keyof T]: Awaited<T[P]>
}>

Convenience function equivalent to suspend(Promise.all(data)).

createSuspense

Because suspend relies on getContext, it must be declared during component initialization. createSuspense can be used to create bind the function in advance, allowing the user to pass references to suspend when access to getContext or $effect would not normally be allowed.

import { createSuspense } from '@svelte-drama/suspense'

const suspend = createSuspense()

The resulting function, suspend, is identical to suspend as listed above.

<Suspense>

<Suspense> provides three slots, only one of which will be displayed at a time. All three are optional.

  • loading: If there any pending requests, this slot will be displayed.
  • error: Once a request fails, this slot is displayed. The caught error is passed to the slot as error.
  • default: After all children have finished loading data, display this.

Two events are availabe:

  • onerror: Triggers after a promise given to suspend is rejected. The original error is passed as part of event.detail.
  • onload: Triggers when all components inside the <Suspense> block have finished loading.
<script>
import { createSuspense, Suspense } from '@svelte-drama/suspense'
const suspend = createSuspense()

const MyComponent = import('./my-component.svelte').then((m) => m.default)
</script>

<Suspense
  onerror={(e) => console.error(e.detail)}
  onload={() => console.log('loaded')}
>
  {#snippet loading()}
    <p>Loading...</p>
  {/snippet}
  {#snippet error(error)}
    <p>Error: {error?.message || error}</p>
  {/snippet}

  {#snippet children(suspend)}
    <h1>My Component</h1>
    {#await suspend(MyComponent) then MyComponent}
      <MyComponent />
    {/await}
  {/snippet}
</Suspense>

<SuspenseList>

<SuspenseList> orchestrates the loading of all child <Suspense> containers. It guarantees they will load in display order. This is useful to avoid multiple, distracting pop-ins of content or reflow of elements while the user is reading.

  • collapse: Boolean. Defaults to false. If true, only one loading state will be shown among the children.
  • final: Boolean. Defaults to false. If true, children will not resuspend if they have been displayed, regardless of the state of previous siblings.
  • onload: Triggers when all components inside the <SuspenseList> have finished loading.
<script>
import { Suspense, SuspenseList } from '@svelte-drama/suspense'
import Loading from './loading.svelte'
import Post from './my-component.svelte'

export let posts
</script>

<SuspenseList collapse final>
  {#snippet children(loading)}
    {#if loading}
      <p>Fetching posts...</p>
    {/if}

    {#each posts as post}
      <Suspense>
        <Post {post} />

        {#snippet loading()}
          <Loading />
        {/snippet}
      </Suspense>
    {/each}
  {/snippet}
</SuspenseList>

Limitations

  • Intro transitions will not work as expected on elements inside the default slot. Elements are rendered in a hidden container as soon as possible, which triggers these intro transitions prematurely.
  • SSR will only display the loading component. Implementing <Suspense> during SSR would require Svelte to support async/await during SSR.
  • createSuspense operates at component boundaries. The following example causes the parent of "my-component.svelte" to suspend, not the <Suspense> block inside of it, despite initial appearances:
<script>
import getData from './get-data.js'
import Suspense, { createSuspense } from '@svelte-drama/suspense'
const suspend = createSuspense()
const request = getData()
</script>

<Suspense>
  {#await suspend(request) then data}
    {JSON.stringify(data)}
  {/await}
</Suspense>

This, however, will work as it looks:

<script>
import getData from './get-data.js'
import Suspense from '@svelte-drama/suspense'
const request = getData()
</script>

<Suspense>
  {#snippet children(suspend)}
    {#await suspend(request) then data}
      {JSON.stringify(data)}
    {/await}
  {/snippet}
</Suspense>