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 🙏

© 2026 – Pkg Stats / Ryan Hefner

svelteprovider

v0.2.0

Published

> A [Riverpod](https://riverpod.dev/docs/introduction/why_riverpod) (from Flutter) inspired state management library for Svelte.

Readme

SvelteProvider

A Riverpod (from Flutter) inspired state management library for Svelte.

SvelteProvider is a Svelte store with the annoying parts handled:

  • Loading & error state is built in, no boilerplate
  • Singletons shared across every component and page, lazy-loaded on first use
  • Providers that depend on other providers re-fetch automatically when upstream data changes
  • Automatic cleanup of instance when no longer used
  • Bbusiness logic grouped together in pods, rather than scattered across components/pages

Installation

npm install svelteprovider

Quick start — functional API

The provider() factory is the simplest way to create a provider.

// lib/providers.ts
import { provider } from 'svelteprovider';

export const postsProvider = provider<Post[]>(async () => {
    const resp = await fetch('/api/posts');
    return resp.json() as Post[];
});
<script lang="ts">
    import { postsProvider } from '$lib/providers';

    const posts = postsProvider();
    const isLoading = posts.isLoading;
    const error = posts.error;
</script>

{#if $isLoading}
    <p>Loading…</p>
{:else if $error}
    <p>Error: {$error.message}</p>
{:else}
    {#each $posts as post}
        <p>{post.title}</p>
    {/each}
{/if}

isLoading, error, and the provider itself are all Svelte stores — prefix with $ to read their current value.


Functional API with dependencies

Declare dependencies and consume their values in one place — no split between a constructor and a build() method.

import { provider } from 'svelteprovider';
import { accountProvider } from './account';

// `account` is typed as IAccount — inferred from accountProvider's type
export const userPostsProvider = provider(
    [accountProvider],
    async (account) => {
        const resp = await fetch(`/api/users/${account.id}/posts`);
        return resp.json() as Promise<Post[]>;
    },
);

When accountProvider emits a new value, userPostsProvider is automatically invalidated and re-fetched.


Parameterised providers

Use paramProvider when the provider needs a reactive runtime parameter. The factory receives each parameter as a Readable<T> that can be passed directly as a dependency. Calling the returned function with new values updates those stores automatically, causing the provider to re-run.

import { paramProvider, provider } from 'svelteprovider';
import type { Readable } from 'svelte/store';

export const postProvider = paramProvider((postId: Readable<string>) =>
    provider([postId], (id) =>
        fetch(`/api/posts/${id}`).then(r => r.json() as Promise<Post>),
    ),
);
<script lang="ts">
    import { postProvider } from '$lib/providers';

    let { postId }: { postId: string } = $props();

    // $derived re-calls postProvider when postId changes,
    // updating the internal store → provider re-fetches automatically
    const post = $derived(postProvider(postId));
</script>

{#if $post}
    <h1>{$post.title}</h1>
    <p>{$post.body}</p>
{/if}

Side effects — actions

Pass an object with a build method and any number of action methods to colocate data fetching and mutations in one place. Each method has this automatically bound to the provider instance, giving access to setState(), invalidate(), promise, and subscribe.

// pods/cart.ts
import { provider } from 'svelteprovider';
import { get } from 'svelte/store';

export const cartProvider = provider({
    async build(): Promise<CartItem[]> {
        return fetchCart();
    },
    async addItem(item: CartItem) {
        const current = get(this) ?? [];
        await this.setState([...current, item]);
    },
    async removeItem(id: string) {
        const current = get(this) ?? [];
        await this.setState(current.filter(i => i.id !== id));
    },
    async refresh() {
        return this.invalidate();
    },
});
<script lang="ts">
    import { cartProvider } from '../pods/cart.js';

    const cart = cartProvider();
</script>

{#each $cart ?? [] as item}
    <p>{item.name} <button onclick={() => cart.removeItem(item.id)}>×</button></p>
{/each}
<button onclick={() => cart.addItem({ id: '1', name: 'Widget' })}>Add item</button>

setState() accepts a plain value or a Promise. get(this) returns the current value synchronously from the svelte store, or null if the provider hasn't resolved yet.


Actions with dependencies

Combine dependencies and actions in the same object. The build method receives the resolved dependency values as arguments.

import { provider } from 'svelteprovider';
import { accountProvider } from './account';
import { get } from 'svelte/store';

export const userPostsProvider = provider(
    [accountProvider],
    {
        async build(account: IAccount) {
            return fetch(`/api/users/${account.id}/posts`).then(r => r.json());
        },
        async refresh() {
            return this.invalidate();
        },
    },
);

Invalidating a provider

Every provider exposes a public invalidate() — triggers a fresh build() and returns a promise that resolves to the new value.

await accountProvider().invalidate();

Class-based API

For providers that need instance state or other advanced patterns, extend Provider directly.

Basic provider

import { Provider } from 'svelteprovider';

class AccountProvider extends Provider<IAccount> {
    constructor() {
        super(null);        // initial value before build() runs
    }
    protected async build(): Promise<IAccount> {
        const resp = await fetch('/api/account', { headers: getAuthHeaders() });
        return resp.json();
    }
}

export const accountProvider = AccountProvider.create();

Depending on another provider

Specify the Deps generic to get fully typed build() parameters.

class AccountIsOldPlanProvider extends Provider<boolean, [], [AccountProvider]> {
    constructor() {
        super(false, accountProvider());
    }
    // `account` is typed as IAccount — no any leaking through
    protected async build(account: IAccount): Promise<boolean> {
        return (account.plan?.products?.length ?? 0) > 0;
    }
}

Parameterised class providers

Pass constructor arguments to scope the singleton cache. Each unique argument combination gets its own instance.

class PostProvider extends Provider<IPost, [string]> {
    constructor(private postId: string) {
        super(null);
    }
    protected async build(): Promise<IPost> {
        return fetch(`/api/posts/${this.postId}`).then(r => r.json());
    }
}

export const postProvider = PostProvider.create();
<script lang="ts">
    let { postId }: { postId: string } = $props();
    const post = $derived(postProvider(postId));
</script>

{#if $post}<h1>{$post.title}</h1>{/if}

Awaiting first load

provider.promise resolves once the first build completes. It returns the same Promise instance for the lifetime of a single build cycle.

{#await posts.promise}
    <p>Loading…</p>
{:then value}
    {#each value as post}<p>{post.title}</p>{/each}
{:catch err}
    <p>Error: {err.message}</p>
{/await}

Returning a Readable (streaming updates)

build() can return a Readable instead of a Promise. The provider forwards every emission as its own value, staying live as long as the store does. When combined with paramProvider, the previous stream is torn down and a fresh one is opened whenever the parameter changes.

import { paramProvider, provider } from 'svelteprovider';
import { readable } from 'svelte/store';
import type { Readable } from 'svelte/store';

// Streams live price updates for a given ticker symbol
export const priceProvider = paramProvider((ticker: Readable<string>) =>
    provider([ticker], (symbol) =>
        readable(0, (set) => {
            const ws = new WebSocket(`wss://prices.example.com/${symbol}`);
            ws.onmessage = (e) => set(JSON.parse(e.data).price);
            return () => ws.close();
        }),
    ),
);

Lazy loading and lifecycle

Providers load lazily on first subscriber. When the last subscriber leaves, cleanup is deferred by one microtask tick. If a new subscriber arrives within that tick — as happens during SvelteKit page navigation — the instance is reused and no re-fetch occurs. If nothing re-subscribes, the provider is destroyed and memory is freed.

To keep a provider alive indefinitely across all unsubscribes:

const p = myProvider();
p.keepAlive = true;

Server-side rendering (SSR)

The library is SSR-safe — no browser globals are accessed at module evaluation time. You can import providers in SvelteKit load functions:

// +page.ts
import { postsProvider } from '$lib/providers';

export async function load() {
    return { posts: await postsProvider().promise };
}

Organising providers — the pods convention

Define providers in plain .ts modules (called "pods") outside your component files. This keeps providers reusable, testable, and easy to discover.

src/
  pods/
    user.ts          ← userProvider, userPostsProvider
    posts.ts         ← postProvider (paramProvider)
  routes/
    +page.svelte     ← imports from pods, calls the factory
// src/pods/user.ts
import { provider } from 'svelteprovider';

export const userProvider = provider(async () => fetchCurrentUser());
<!-- +page.svelte -->
<script lang="ts">
  import { userProvider } from '../pods/user.js';
  const user = userProvider();
</script>

Todo

  • [ ] Server-side testing
  • [ ] Graceful error propagation from parent to child providers