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

nucleux

v1.4.0

Published

Simple, atomic hub for all your React application's state management needs. No providers, no boilerplate, just state that works.

Readme


Why Nucleux?

  • Zero boilerplate - Write less, do more
  • No providers - Use state anywhere without wrapping components
  • Atomic updates - Only subscribed components re-render
  • Framework agnostic - Works with or without React

Installation

npm install nucleux

Quick Start

Create a store with atomic state:

import { Store } from 'nucleux';

class CounterStore extends Store {
  count = this.atom(0);

  increment() {
    this.count.value += 1;
  }
}

Use it in React components:

import { useStore, useValue } from 'nucleux';

function Counter() {
  const store = useStore(CounterStore);
  const count = useValue(store.count);

  return <button onClick={store.increment}>Count: {count}</button>;
}

That's it! No providers, no reducers, no dispatch.

Core Concepts

Atoms

Atoms are reactive pieces of state. When you change an atom's value, only components subscribed to that specific atom will re-render.

class TodoStore extends Store {
  todos = this.atom([]);
  filter = this.atom('all');

  addTodo(text) {
    this.todos.value = [
      ...this.todos.value,
      { id: Date.now(), text, done: false },
    ];
  }
}

Three Ways to Use State

1. useStore - Get store methods

const todoStore = useStore(TodoStore);
// Access: todoStore.addTodo(), todoStore.toggleTodo(), etc.

2. useValue - Subscribe to specific atoms

const todos = useValue(todoStore.todos);
// Or directly: const todos = useValue(TodoStore, 'todos');

3. useNucleux - Get everything at once

const todo = useNucleux(TodoStore);
// Access: todo.todos, todo.filter, todo.addTodo(), etc.

Advanced Features

Memoization

Further improve atom updates and component re-renders:

class AppStore extends Store {
  // Shallow memoization (default) - compares by reference
  count = this.atom(0);

  // Deep memoization - compares object content
  user = this.atom(
    { name: 'John', age: 30 },
    { memoization: { type: 'deep' } },
  );

  // Custom memoization - use your own comparison logic
  product = this.atom(
    { name: 'Laptop', price: 999.99, discount: 10 },
    {
      memoization: {
        type: 'custom',
        compare: (a, b) => a.name === b.name && a.price === b.price,
      },
    },
  );
}

Works with derived atoms too:

class TodoStore extends Store {
  todos = this.atom([]);
  filter = this.atom('all');

  // Further improve updates when filtered result content is the same
  filteredTodos = this.deriveAtom(
    [this.todos, this.filter],
    (todos, filter) => todos.filter((t) => t.status === filter),
    { type: 'deep' },
  );
}

Persistence

Save state automatically:

class UserStore extends Store {
  // Simple persistence
  theme = this.atom('dark', { persistence: { persistKey: 'theme' } });

  // With custom storage
  profile = this.atom(
    { name: '', email: '' },
    {
      persistence: {
        persistKey: 'profile',
        storage: AsyncStorage,
      },
    },
  );
}

Derived State

Compute values from multiple atoms:

class TodoStore extends Store {
  todos = this.atom([]);
  filter = this.atom('all');

  filteredTodos = this.deriveAtom(
    [this.todos, this.filter],
    (todos, filter) => {
      if (filter === 'done') return todos.filter((t) => t.done);
      if (filter === 'pending') return todos.filter((t) => !t.done);
      return todos;
    },
  );
}

Store Dependencies

Inject other stores:

class NotificationStore extends Store {
  userStore = this.inject(UserStore);
  notifications = this.atom([]);

  constructor() {
    super();
    this.watchAtom(this.userStore.currentUser, (user) => {
      if (user) this.loadNotifications(user.id);
    });
  }
}

Custom Storage (React Native)

Set storage for entire store:

import AsyncStorage from '@react-native-async-storage/async-storage';

class AppStore extends Store {
  storage = AsyncStorage;

  settings = this.atom(
    { notifications: true },
    { persistence: { persistKey: 'settings' } },
  );
}

Or per atom:

class AppStore extends Store {
  settings = this.atom(
    { notifications: true },
    {
      persistence: {
        persistKey: 'settings',
        storage: AsyncStorage,
      },
    },
  );
}

Debugging

Track atom changes during development:

class TodoStore extends Store {
  todos = this.atom([]);
  filter = this.atom('all');

  constructor() {
    super();

    // Enable debugging in development
    if (process.env.NODE_ENV === 'development') {
      this.enableDebug();
    }
  }

  addTodo(text) {
    this.todos.value = [...this.todos.value, { id: Date.now(), text }];
  }
}

Reset Functionality

Reset atoms to their initial values and clear persisted data when needed.

Individual Atom Reset

class UserStore extends Store {
  theme = this.atom('light', { persistence: { persistKey: 'theme' } });
  username = this.atom('guest');
}

const userStore = useStore(UserStore);

// Reset atom to initial value and clear storage
await userStore.theme.reset();

// Reset value but keep persisted data
await userStore.theme.reset({ clearPersisted: false });

// Clear persisted data but keep current value
await userStore.theme.reset({ resetValue: false });

Store-Level Reset

class AppStore extends Store {
  theme = this.atom('light', { persistence: { persistKey: 'theme' } });
  language = this.atom('en', { persistence: { persistKey: 'language' } });
  isOnline = this.atom(true); // No persistence
}

const appStore = useStore(AppStore);

// Reset all atoms (values + persistence)
await appStore.reset();

// Reset specific atoms only
await appStore.reset({ atomKeys: ['theme', 'language'] });

// Convenience methods
await appStore.clearPersistedData(); // Clear persisted data only
await appStore.resetValues(); // Reset values only

Common Use Cases

// User logout - clear sensitive data
await userStore.reset({
  atomKeys: ['profile', 'preferences'],
  resetValues: true,
  clearPersisted: true,
});

// App settings reset
await settingsStore.reset();

React Native Setup

Install the polyfill and import it before Nucleux:

npm install react-native-get-random-values
// App.js - Import this first!
import 'react-native-get-random-values';
import { useStore, useValue } from 'nucleux';

Complete Example

import React from 'react';
import { Store, useNucleux } from 'nucleux';

class TodoStore extends Store {
  todos = this.atom([]);

  addTodo(text) {
    const newTodo = { id: Date.now(), text, done: false };
    this.todos.value = [...this.todos.value, newTodo];
  }

  toggleTodo(id) {
    this.todos.value = this.todos.value.map((todo) =>
      todo.id === id ? { ...todo, done: !todo.done } : todo,
    );
  }
}

function TodoApp() {
  const { todos, addTodo, toggleTodo } = useNucleux(TodoStore);
  const [input, setInput] = React.useState('');

  const handleAdd = () => {
    if (input.trim()) {
      addTodo(input.trim());
      setInput('');
    }
  };

  return (
    <div>
      <div>
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Add todo..."
        />
        <button onClick={handleAdd}>Add</button>
      </div>

      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>
            <label>
              <input
                type="checkbox"
                checked={todo.done}
                onChange={() => toggleTodo(todo.id)}
              />
              {todo.text}
            </label>
          </li>
        ))}
      </ul>
    </div>
  );
}

Try It Live

View on CodeSandbox

API Reference

Store Methods

this.atom(initialValue, options?)

Create reactive state.

Options:

  • persistence - Auto-save to storage
    • persistKey: string - Storage key
    • storage?: SupportedStorage - Custom storage (defaults to localStorage)
  • memoization - Control when updates trigger
    • type: 'shallow' | 'deep' | 'custom' - Comparison strategy
    • compare?: (a, b) => boolean - Custom comparator (for type: 'custom')
// Simple atom
count = this.atom(0);

// With persistence
theme = this.atom('dark', {
  persistence: { persistKey: 'app-theme' },
});

// With memoization
user = this.atom(
  { name: 'John' },
  {
    memoization: { type: 'deep' },
  },
);

// Combined options
profile = this.atom(
  { name: '', email: '' },
  {
    persistence: { persistKey: 'profile' },
    memoization: { type: 'deep' },
  },
);

this.deriveAtom(atoms[], computeFn, memoization?)

Create computed state that updates when source atoms change.

filteredTodos = this.deriveAtom(
  [this.todos, this.filter],
  (todos, filter) => todos.filter((t) => t.status === filter),
  { type: 'deep' }, // Optional memoization
);

this.inject(StoreClass)

Inject another store as a dependency.

userStore = this.inject(UserStore);

this.watchAtom(atom, callback, immediate?)

Watch atom changes within the store.

constructor() {
  super();
  this.watchAtom(this.user, (newUser, prevUser) => {
    console.log('User changed:', newUser);
  });
}

this.enableDebug()

Enable console logging for all atom changes in the store.

constructor() {
  super();

  // Enable debugging in development
  if (process.env.NODE_ENV === 'development') {
    this.enableDebug();
  }
}

atom.reset(options?)

Reset atom to initial value and/or clear persisted data.

Options:

  • resetValue?: boolean - Reset value to initial (default: true)
  • clearPersisted?: boolean - Clear persisted data from storage (default: true)
// Reset everything
await userAtom.reset();

// Reset value only
await userAtom.reset({ clearPersisted: false });

// Clear storage only
await userAtom.reset({ resetValue: false });

this.reset(options?)

Reset multiple atoms in the store.

Options:

  • resetValues?: boolean - Reset values to initial (default: true)
  • clearPersisted?: boolean - Clear persisted data (default: true)
  • atomKeys?: string[] - Specific atoms to reset (default: all atoms)
// Reset all atoms
await this.reset();

// Reset specific atoms
await this.reset({ atomKeys: ['theme', 'language'] });

// Custom combination
await this.reset({
  resetValues: false,
  clearPersisted: true,
  atomKeys: ['cache'],
});

this.clearPersistedData()

Clear all persisted data without affecting current values.

await this.clearPersistedData();

this.resetValues()

Reset all atom values without affecting persisted data.

await this.resetValues();

React Hooks

useStore(StoreClass)

Get store instance with methods.

const todoStore = useStore(TodoStore);
todoStore.addTodo('New task');

useValue(atom) or useValue(StoreClass, 'atomKey')

Subscribe to atom value.

// Direct atom access
const todos = useValue(todoStore.todos);

// Store + key access
const todos = useValue(TodoStore, 'todos');

useNucleux(StoreClass)

Get all methods and atom values.

const { todos, addTodo, removeTodo } = useNucleux(TodoStore);

Requirements: Node ≥14, React ≥16.9.0 (optional)

Author

Marty Roque

License

ISC License

Copyright © 2025 Marty Roque.