Skip to content

Svelte Logo Svelte Integration

The @asyncflowstate/svelte package provides Svelte stores with $ auto-subscription for seamless reactivity.

Installation

bash
npm install @asyncflowstate/svelte @asyncflowstate/core

Core Store (createFlow)

The createFlow feature generates a reactive Svelte store that tracks all asynchronous states inherently. It integrates perfectly with Svelte's auto-subscription $ syntax.

Example

svelte
<script>
  import { createFlow } from '@asyncflowstate/svelte';

  const flow = createFlow(
    async (id) => {
      const response = await fetch(`/api/users/${id}`);
      return response.json();
    },
    {
      onSuccess: (user) => console.log('Fetched:', user.name),
      retry: { maxAttempts: 2 },
    }
  );
</script>

<button
  on:click={() => flow.execute('user-123')}
  disabled={$flow.loading}
>
  {$flow.loading ? 'Loading...' : 'Fetch User'}
</button>

{#if $flow.error}
  <p class="error">{$flow.error.message}</p>
{/if}

{#if $flow.data}
  <div class="user-card">
    <h2>{$flow.data.name}</h2>
    <p>{$flow.data.email}</p>
  </div>
{/if}

Store Properties

Access via $flow auto-subscription:

PropertyTypeDescription
$flow.statusstringCurrent flow state
$flow.loadingbooleanLoading indicator
$flow.dataT | nullLast result
$flow.errorError | nullLast error

Methods

Call directly on the store (not via $):

svelte
<script>
  // Execute
  flow.execute(args);

  // Reset
  flow.reset();
</script>

Form Handling

Handle form submissions and reactive data updates directly inside Svelte components without boilerplate.

Example

svelte
<script>
  import { createFlow } from '@asyncflowstate/svelte';

  const flow = createFlow(async (data) => {
    return await fetch('/api/profile', {
      method: 'POST',
      body: JSON.stringify(data),
    }).then(r => r.json());
  });

  function handleSubmit(e) {
    const formData = new FormData(e.target);
    flow.execute(Object.fromEntries(formData));
  }
</script>

<form on:submit|preventDefault={handleSubmit}>
  <input name="name" placeholder="Name" />
  <input name="email" type="email" placeholder="Email" />

  <button type="submit" disabled={$flow.loading}>
    {$flow.loading ? 'Saving...' : 'Save'}
  </button>

  {#if $flow.error}
    <p class="error">{$flow.error.message}</p>
  {/if}

  {#if $flow.success}
    <p class="success">Saved successfully!</p>
  {/if}
</form>

Advanced Patterns

By leveraging Svelte's reactive declarations ($:), you can create deeply integrated search workflows with built-in concurrency modifiers and debounce mechanics.

Example

svelte
<script>
  import { createFlow } from '@asyncflowstate/svelte';

  const searchFlow = createFlow(
    async (query) => {
      const res = await fetch(`/api/search?q=${query}`);
      return res.json();
    },
    { concurrency: 'restart', debounce: 300 }
  );

  let query = '';
  $: if (query.length > 2) searchFlow.execute(query);
</script>

<input bind:value={query} placeholder="Search..." />

{#if $searchFlow.loading}
  <p>Searching...</p>
{/if}

{#each $searchFlow.data ?? [] as item}
  <div>{item.name}</div>
{/each}

Best Practices

Building a professional async experience in Svelte requires leveraging stores and reactive declarations effectively. Follow these patterns for the best results.

Reactive Patterns

Stores are the key

The createFlow result is a standard Svelte store. Always use the $ prefix to subscribe to the state in your markup. This ensures that Svelte automatically manages subscriptions and unsubscriptions for you.

svelte
<button disabled={$flow.loading}>
  {$flow.loading ? '...' : 'Submit'}
</button>

Use reactive declarations sparingly

While $: searchFlow.execute(query) is powerful, ensure you use concurrency: 'restart' and debounce to prevent excessive network requests when the user types quickly.

Global Configuration

Context API for configuration

In Svelte, there is no direct equivalent to provide/inject. Instead, we recommend creating a shared stores/flows.ts file or using the Svelte setContext/getContext pattern to share a global configuration object across your component tree.

User Experience (UX)

Rule of 400ms

Next JS or React actions can feel "jittery" if they return too quickly, causing a loading spinner to flash for just a few frames. Use loading: { minDuration: 400 } to ensure your UI feels stable and predictable.

Graceful Error States

Always use {#if $flow.error} to provide clear, accessible feedback. For a premium experience, combine this with a toast notification in the onError callback of your flow definition.

Built with by AsyncFlowState Contributors
Open Source · MIT License