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

@fr0st/state

v1.0.1

Published

Small reactive state primitives for values, effects, and keyed stores.

Readme

FrostState

CI npm version npm downloads minzipped size license

Small, focused reactive state primitives for values, effects, and keyed stores. FrostState has zero runtime dependencies, works in Node and bundlers, and also ships a browser-friendly UMD bundle that exposes globalThis.State.

Highlights

  • Named exports for tree-shaking
  • Browser UMD bundle in dist/
  • No runtime dependencies
  • JSDoc-powered IntelliSense

Installation

Node / bundlers

npm i @fr0st/state

FrostState is ESM-only. Use import syntax in Node and bundlers.

Browser (UMD)

Load the bundle from your own copy or a CDN:

<script src="/path/to/dist/frost-state.min.js"></script>
<!-- or -->
<script src="https://cdn.jsdelivr.net/npm/@fr0st/state@latest/dist/frost-state.min.js"></script>
<script>
    const { StateStore, useEffect, useState } = globalThis.State;

    const count = useState(0);

    useEffect(() => {
        console.log('count =', count());
    });

    count(1);
</script>

Quick Start

Reactive values

import { useEffect, useState } from '@fr0st/state';

const first = useState('Ada');
const last = useState('Lovelace');

useEffect(() => {
    console.log(`${first()} ${last()}`);
});

last('Byron'); // logs "Ada Byron" on the next microtask
first.value = 'Augusta'; // logs "Augusta Byron"

Keyed stores

import { StateStore, useEffect } from '@fr0st/state';

const store = StateStore.wrap({
    count: 0,
});

useEffect(() => {
    console.log('count =', store.count);
});

store.count = 1; // logs "count = 1"

TypeScript note: FrostState is written in JavaScript and uses JSDoc types, which most editors surface as IntelliSense.

API

FrostState exports three named APIs from @fr0st/state: useState, useEffect, and StateStore.

useState(value)

Creates a callable state accessor for a single value.

const state = useState(value);

The returned accessor supports:

  • state(): read the current value
  • state(next): write the current value
  • state.get(markEffects = true): read the current value, optionally without effect tracking
  • state.set(next): write the current value
  • state.value: read or write the current value
  • state.previous: read the previous value after the last successful change
import { useState } from '@fr0st/state';

const state = useState('hello');

state(); // 'hello'
state('world');

state.get(); // 'world'
state.set('again');

state.value = 'done';
state.previous; // 'again'

useEffect(callback, options)

Runs an effect immediately, tracks the states read during that run, and schedules re-runs when any of those states change.

const effect = useEffect(callback, options);

Options:

  • options.weak: use a WeakRef-backed runner

The returned runner supports:

  • effect(): schedule a re-run in a microtask
  • effect.sync(): run immediately
import { useEffect, useState } from '@fr0st/state';

const a = useState(1);
const b = useState(2);

const effect = useEffect(() => {
    console.log(a() + b());
});

a(3); // logs 5 on the next microtask
effect.sync(); // logs immediately

StateStore

Creates a callable, proxy-backed keyed store for state accessors. Property reads return existing keys, property assignment writes keys, and missing property reads return undefined. Effects that read missing keys subscribe to later assignment without exposing those keys through enumeration.

const store = new StateStore();
const state = store(key, defaultValue);

Instance API

The returned store supports:

  • store.key: read an existing key
  • store.key = value: write a key
  • store.use(key, defaultValue): retrieve or create a state accessor
  • store(key, defaultValue): retrieve or create a state accessor through the callable form
  • store.set(object): set top-level keys from an object
  • store.has(key): check whether a key exists
  • store.keys(): iterate stored keys
import { StateStore, useEffect } from '@fr0st/state';

const store = new StateStore();
const count = store('count', 0);

store.set({ label: 'Clicks' });

useEffect(() => {
    console.log(store.label, count());
});

count(1); // logs "Clicks 1"
store.count = 2; // logs "Clicks 2"

store.has('count'); // true
Array.from(store.keys()); // ['count', 'label']

Static helpers

  • StateStore.wrap(value, options): wrap a plain object in a store
  • StateStore.merge(store, value, options): merge plain-object data into a store
import { StateStore } from '@fr0st/state';

const nested = StateStore.wrap(
    {
        user: {
            name: 'Ada',
        },
    },
    { deep: true },
);

nested.user.name = 'Grace';

const settings = new StateStore();

StateStore.merge(
    settings,
    {
        ui: {
            theme: 'dark',
        },
    },
    { deep: true },
);

StateStore.merge(
    settings,
    {
        ui: {
            compact: true,
        },
    },
    { deep: true },
);

settings.ui.theme = 'light';

nested.user.name; // 'Grace'
settings.ui.theme; // 'light'
settings.ui.compact; // true

Behavior Notes

  • useEffect() tracks only the states read during the latest successful run.
  • useEffect() schedules normal re-runs in a microtask, and .sync() bypasses that scheduling.
  • store.set(...) assigns top-level keys only. Nested plain objects remain plain values.
  • Use StateStore.wrap(..., { deep: true }) or StateStore.merge(..., { deep: true }) for nested reactive stores.
  • Missing property reads such as store.missing return undefined. Reads made during effect tracking still subscribe to later assignment without exposing the key.
  • API keys such as use, set, has, and keys are reserved and cannot be used as state keys.
  • Weak effects rely on WeakRef. The test suite uses node --expose-gc to cover weak-reference behavior.

Development

npm test
npm run js-lint
npm run build

License

FrostState is released under the MIT License.