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

svatoms

v0.2.0

Published

Svelte model data flow utilities with selectors.

Readme

svatoms

Svelte's utilities for model data flow: provide a model once, then consume slices of it anywhere with minimal updates. The API mirrors the idea of React Jotai “provider + selector”, but uses Svelte context + stores instead of React providers.

Install

pnpm add svatoms

Why this exists

Inspired by Jojoo and Shiro, I wanted a Svelte-native way to handle complex model data flow with:

  • a single entry point (one SSR load() result)
  • minimal-granularity reads via selectors
  • centralized business data to avoid scattered requests

svatoms keeps it idiomatic to Svelte:

  • use Svelte setContext/getContext instead of provider components
  • use Svelte stores instead of hooks
  • keep SSR-safe local scope by default
  • allow optional global sharing

The exact problem this solves (SvelteKit version)

Typical SvelteKit pain points:

  • load() data needs to be used across many layers
  • prop-drilling becomes noisy quickly 😭
  • many components only need tiny fields, but full models trigger extra updates

svatoms maps the pattern into SvelteKit cleanly:

  • single entry: call mountModelData once in +page.svelte or +layout.svelte
  • minimal slices: child components use selectModelData for only useful fields
  • no scattered fetching: all data comes from load()

Minimal SvelteKit wiring:

// src/routes/posts/[slug]/+page.server.ts
export const load = async ({ params }) => {
  const post = await fetchPost(params.slug)
  return { post }
}
// src/lib/post-context.ts
import { createModelDataContext } from 'svatoms'

export type Post = {
  title: string
  likes: number
  // ...
}

export const postContext = createModelDataContext<Post>({ name: 'post' })
<!-- src/routes/posts/[slug]/+page.svelte -->
<script lang="ts">
  import { postContext } from '$lib/post-context'
  let { data } = $props()

  // Recommended in Svelte 5 runes mode:
  // pass a getter to keep model data in sync automatically.
  postContext.mountModelData(() => data.post)
</script>

<slot />
<!-- Any child component -->
<script lang="ts">
  import { postContext } from '$lib/post-context'

  const title = postContext.selectModelData((p) => p?.title ?? '')
  const likeCount = postContext.selectModelData((p) => p?.likes ?? 0)
</script>

<h1>{$title}</h1>
<span>{$likeCount}</span>

If you want the model to survive nested route changes, mount it in +layout.svelte instead of +page.svelte.

When the model data changes (e.g. user likes a post), use setModelData or updateModelData to update it in one place, and all selectors will update accordingly.

Important: context usage in event handlers

getContext(...) can only be called during component initialization. That means calling setModelData / updateModelData / getModelData directly inside event handlers will throw.

Bind actions once during initialization, then use them in events:

<script lang="ts">
  import { postContext } from '$lib/post-context'

  const { updateModelData } = postContext.useModelActions() // don't forget this!

  const like = () => {
    updateModelData((prev) =>
      prev ? { ...prev, likes: prev.likes + 1 } : prev
    )
  }
</script>

SvelteKit data flow (SSR → UI)

1) Load data on the server

// +page.server.ts
export const load = async ({ params }) => {
  const post = await fetchPost(params.slug)
  return { post }
}

2) Provide the model once

<!-- +page.svelte -->
<script lang="ts">
  import { postContext } from '$lib/post-context'
  let { data } = $props()

  postContext.mountModelData(() => data.post)
</script>

<slot />

Why getter? On SvelteKit client-side navigation, the component may stay mounted while only data changes. Passing a getter keeps context store synced automatically.

3) Select slices anywhere

<script lang="ts">
  import { postContext } from '$lib/post-context'

  const meta = postContext.selectModelData((p) => ({
    title: p?.title,
    likes: p?.likes ?? 0,
  }))
</script>

<h1>{$meta.title}</h1>
<p>Likes: {$meta.likes}</p>

4) Update the model from anywhere

<script lang="ts">
  import { postContext } from '$lib/post-context'

  const { updateModelData } = postContext.useModelActions()

  const like = () => {
    updateModelData((prev) =>
      prev ? { ...prev, likes: prev.likes + 1 } : prev
    )
  }
</script>

<button onclick={like}>Like</button>

Plain Svelte usage

<script lang="ts">
  import { createModelDataContext } from 'svatoms'

  type User = { id: string; name: string; role: string }
  const userContext = createModelDataContext<User>({ name: 'user' })

  let user = $state<User>({ id: '1', name: 'Ada', role: 'admin' })

  userContext.mountModelData(() => user)

  const { updateModelData } = userContext.useModelActions()

  const name = userContext.selectModelData((u) => u?.name ?? 'Unknown')

  // Update user name
  const rename = () => {
    updateModelData((prev) =>
      prev ? { ...prev, name: 'Grace Hopper' } : prev
    )
  }
</script>

<p>User: {$name}</p>

API

createModelDataContext<Model>(options?)

Create a context manager for a specific model type.

Options

  • name?: string – used to label the internal symbol (for debugging)
  • key?: symbol – custom context key (advanced)
  • initial?: Model | null – initial value for the global store
  • defaultScope?: 'local' | 'global' – default store scope

Returns

  • provideModelData(data, opts?)
    • Set context + write initial data (no auto cleanup)
  • mountModelData(dataOrSource, opts?)
    • Same as provideModelData, but resets to null on destroy
    • Supports static value, getter () => value, or Readable store input
    • Static value input is supported for compatibility, but marked deprecated in TypeScript
  • provideModelStore(store)
    • Inject a custom store directly
  • useModelStore(fallback?)
    • Get the current store (default fallback is global)
  • useModelActions()
    • Bind store actions during component initialization (safe for events)
  • selectModelData(selector, opts?)
    • Create a derived store from a selector
  • setModelData(value) / updateModelData(fn) / getModelData()
    • Convenience helpers for the current store
  • setGlobalModelData(valueOrUpdater) / getGlobalModelData()
    • Global store helpers
  • syncModelData(store, data)
    • Explicitly push new data into a store

selectModelData options

selectModelData(selector, {
  equals?: (a, b) => boolean // default: Object.is
})

// selector: (model: Model | null) => Result

Use equals to avoid re-renders when your selector returns derived objects.

Local vs global scope

  • local (default): data is scoped to the component tree that called mountModelData. This is SSR-safe.
  • global: data is shared across the whole app, similar to a singleton store.

You can override per call:

postContext.mountModelData(() => data.post, { scope: 'global' })

Notes

  • Call mountModelData during component initialization (top-level of <script>), not inside functions.
  • Svelte 5 runes mode uses $effect; in Svelte 4 you can use $: instead.
  • In Svelte 5 runes mode, prefer mountModelData(() => data.xxx) to avoid state_referenced_locally warnings.
  • If you pass a static value, use syncModelData or store.set() when your data changes after navigation.

License

MIT License © 2026 grtsinry43