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

@minute-spa/appstate

v0.0.2

Published

A simple vanilla app state service.

Readme

AppState

⚠️ DEPRECATED: This package is deprecated. Please use the public MinSPA repository for the newest code: https://github.com/devosm1030/minspa

A lightweight, flexible state management library for JavaScript applications with built-in persistence support using browser sessionStorage.

This module is a component of the Minute SPA framework (https://github.com/devosm1030/minute-spa), but is an standalone component that can be used independently of the framework.

Zero dependencies - use in your project via NPM, or simply clone the code right into your project for a truly vanilla experience that requires no packagers or bundlers!

Table of Contents

Overview

AppState provides a simple yet powerful way to manage application state with a publish-subscribe pattern. It supports multiple isolated state instances, automatic persistence to sessionStorage, and proxy-based direct property access.

Key Features

  • Event-based state management - Subscribe to state changes with callback functions
  • Automatic persistence - Optional sessionStorage integration when used in browser environment for tab-lifetime persistence
  • Multiple state instances - Create isolated state contexts with unique identifiers
  • Proxy support - Access state directly as object properties or through methods
  • Browser and Node.js compatible - Gracefully handles environments without window object

Installation and Usage

For NodeJS projects

Installation

In your project directory, install the dependency on the command line:

npm install --save @minute-spa/appstate

Usage

Import the package in your code:

import { appState } from '@minute-spa/appstate'

For Vanila projects

Vanilla Installation

Clone https://github.com/devosm1030/minute-spa/blob/main/packages/AppState/index.js into your project and rename as appropriate.

Vanilla Usage

From an ES module, import the package in your code:

import { appState } from '<path/to/your/file>'

API Reference

Default state service: appState

The default appState instance that can be used globally in your application.

import { appState } from '@minute-spa/appstate';

appStateFor(stateId)

Creates or retrieves an isolated AppState instance with a unique identifier. When you want to create multiple app state services within your application.

Parameters:

  • stateId (string, required) - Unique identifier for the state instance

Returns: AppState instance (proxied)

Throws: Error if stateId is not provided

Example:

import { appStateFor } from '@minute-spa/appstate';

const authState = appStateFor('auth');
const uiState = appStateFor('ui');

// These states are completely isolated
authState.set('token', 'abc123');
uiState.set('sidebarOpen', true);

console.log(authState.get('token')); // 'abc123'
console.log(uiState.get('token')); // undefined

appState.on(eventName, callback)

Subscribe to state changes for a specific event.

Parameters:

  • eventName (string, required) - Name of the event to subscribe to
  • callback (function, required) - Function called when state changes. Receives the new state value as argument.

Returns: undefined

Throws: Error if eventName is not a string

Behavior:

  • If state already exists for the event, callback is immediately called with current value
  • Multiple callbacks can subscribe to the same event
  • Callbacks are executed in the order they were registered
  • Callbacks are executed on EVERY request to update the event, regardless of whether or not the event or it's contents have changed.

Example:

// Subscribe to user changes
appState.on('user', (userData) => {
  console.log('User changed:', userData);
});

// Subscribe multiple callbacks to same event
appState.on('user', (userData) => {
  updateUI(userData);
});

appState.on('user', (userData) => {
  logAnalytics('user_changed', userData);
});

// Set user - all callbacks will be triggered
appState.set('user', { id: 1, name: 'Alice' });

Immediate callback execution:

// Set state first
appState.set('config', { theme: 'dark' });

// Subscribe later - callback is immediately called with current value
appState.on('config', (config) => {
  console.log('Current config:', config); // Logs immediately
});

appState.off(eventName, callback?)

Unsubscribe from state changes.

Parameters:

  • eventName (string, required) - Name of the event to unsubscribe from
  • callback (function, optional) - Specific callback to remove. If omitted, all callbacks for the event are removed.

Returns: undefined

Throws: Error if eventName is not a string

Example:

const userHandler = (user) => {
  console.log('User:', user);
};

// Subscribe
appState.on('user', userHandler);

// Unsubscribe specific callback
appState.off('user', userHandler);

// Or remove all subscribers for an event
appState.on('user', handler1);
appState.on('user', handler2);
appState.off('user'); // Removes both handler1 and handler2

appState.set(eventName, data, persist?)

Set or update state and notify subscribers. Subscribers will be notified regardless of whether or not the data for the event has changed from it's prior value.

Parameters:

  • eventName (string, required) - Name of the event/state key
  • data (any, required) - Value to store (must be JSON-serializable if persisting)
  • persist (boolean, optional, default: false) - Whether to persist to sessionStorage. Only applicable in a browser envrionment.

Returns: The data value that was set, or undefined if parameters are invalid

Throws: Error if eventName is not a string

Example:

// Basic usage
appState.set('count', 42);

// With persistence (survives page refresh within same tab)
appState.set('userPreferences', { theme: 'dark', language: 'en' }, true);

// Complex data structures
appState.set('todos', [
  { id: 1, text: 'Learn AppState', done: true },
  { id: 2, text: 'Build app', done: false }
]);

// Chainable pattern (returns the data)
const savedUser = appState.set('user', { name: 'Bob' });
console.log(savedUser); // { name: 'Bob' }

Persistence behavior:

// First time setting with persist
appState.set('token', 'secret123', true);

// Refresh page - state is restored from sessionStorage

// Update persisted state (automatically stays persisted)
appState.set('token', 'newSecret456'); // Still persisted!

// To stop persisting, you must delete and re-set without persist flag
appState.delete('token');
appState.set('token', 'nonPersisted');

appState.get(eventName)

Retrieve current state value.

Parameters:

  • eventName (string, required) - Name of the event/state key

Returns: The stored value, or undefined if not found

Throws: Error if eventName is not a string

Example:

appState.set('config', { apiUrl: 'https://api.example.com' });

const config = appState.get('config');
console.log(config.apiUrl); // 'https://api.example.com'

// Non-existent keys return undefined
const missing = appState.get('nonExistent'); // undefined

appState.delete(eventName, broadcast?)

Remove state and optionally notify subscribers.

Parameters:

  • eventName (string, required) - Name of the event/state key to delete
  • broadcast (boolean, optional, default: false) - Whether to notify subscribers with undefined

Returns: undefined

Example:

appState.set('temp', 'temporary data');
console.log(appState.get('temp')); // 'temporary data'

// Delete without notifying subscribers
appState.delete('temp');
console.log(appState.get('temp')); // undefined

// Delete with notification
appState.on('user', (user) => {
  console.log('User is now:', user);
});

appState.set('user', { name: 'Charlie' }); // Logs: "User is now: { name: 'Charlie' }"
appState.delete('user', true); // Logs: "User is now: undefined"

appState.reset()

Clear all state and subscribers, returning to initial state.

Parameters: None

Returns: undefined

Example:

appState.set('key1', 'value1');
appState.set('key2', 'value2', true); // persisted
appState.on('key1', () => {});

appState.reset();

// All state cleared
console.log(appState.get('key1')); // undefined
console.log(appState.get('key2')); // undefined (even persisted data is removed)

// All subscribers removed
// Subscribers will not be called

Proxy Property Access

AppState instances support direct property access through JavaScript Proxy.

Example:

// Setting values
appState.userName = 'Alice';
appState.isLoggedIn = true;
appState.settings = { theme: 'dark' };

appState.on('userName', userName => {
  console.log(userName); // 'Alice'
})

// Getting values
console.log(appState.userName); // 'Alice'
console.log(appState.isLoggedIn); // true

// Equivalent to:
appState.set('userName', 'Alice');
appState.get('userName');

// Note: Proxy access does NOT support persistence flag
// Use explicit set() method for persistence
appState.set('userName', 'Alice', true); // Persisted

Advanced Usage

Multiple State Instances

Create isolated state contexts for different parts of your application:

import { appStateFor } from '@minute-spa/appstate';

const authState = appStateFor('auth');
const cartState = appStateFor('cart');
const notificationState = appStateFor('notifications');

// Each instance is completely independent
authState.set('token', 'jwt-token-here', true);
cartState.set('items', [], true);
notificationState.set('unread', 0);

Persistence Patterns

// User preferences that survive page refresh
appState.set('preferences', {
  theme: 'dark',
  language: 'en',
  notifications: true
}, true);

// Session-only data (not persisted)
appState.set('tempFormData', { field1: 'value' });

Cleanup and Memory Management

const handler = (data) => console.log(data);

// Subscribe
appState.on('event', handler);

// Clean up when component unmounts
function cleanup() {
  appState.off('event', handler);
}

// Or reset everything (useful for testing)
afterEach(() => {
  appState.reset();
});

Browser Compatibility

AppState works in all modern browsers and Node.js environments:

  • Browser: Requires sessionStorage support (IE 8+, all modern browsers)
  • Node.js: Works without browser-specific features (persistence disabled)

License

MIT License - See notice in index.js comments.