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 🙏

© 2025 – Pkg Stats / Ryan Hefner

react-unprop

v1.0.8

Published

A lightweight React signal system for sharing reactive state across components — no prop drilling, no context, no global state libraries. Just pure reactivity with persistent storage.

Readme

react-unprop

A tiny, open-source React signal system for reactive state sharing — no props, no context.

Fast, flexible, and works across your entire React tree.

npm version License: MIT TypeScript

✨ Features

  • Fast reactive updates — re-renders only when signals change
  • 🧼 No props. No context. Just unprops
  • 🎯 Auto re-render on signal update
  • 🔁 Share state between siblings, parents, or anywhere
  • 🔄 Cross-tab synchronization — keep state in sync across browser tabs
  • 🖥️ SSR compatible — works with Next.js and other SSR frameworks
  • Works with primitives, arrays, and objects
  • 🔐 Optional encrypted persistence via localStorage
  • 🧩 Encryption built-in — no config needed
  • 🧠 Environment-agnostic — works with CRA, Vite, Next.js, etc.
  • 💙 Supports both JavaScript and TypeScript
  • 📦 Tiny bundle size — less than 20KB gzipped
  • 🚀 Zero dependencies
  • 🌍 Open Source — built by the community, for the community

📦 Installation

Install using npm:

npm install react-unprop

or using yarn:

yarn add react-unprop

or using pnpm:

pnpm add react-unprop

🚀 Quick Start

Create and use react-unprops just like global useState — but better.

import { createSignal, useSignal } from "react-unprop";

const counter = createSignal(0);

function Counter() {
  const count = useSignal(counter);
  return <h1>{count}</h1>;
}

function IncrementButton() {
  return <button onClick={() => counter.set((c) => c + 1)}>Increment</button>;
}

✅ Both components update automatically when the signal changes.

🔐 Persisting Encrypted State (Optional)

react-unprop allows optional persistence to localStorage with built-in AES encryption.

Example

// Persistent but not encrypted
const userSignal = createSignal({ name: "Alice" }, { persistKey: "user" });

// Persistent and encrypted
const sensitiveDataSignal = createSignal(
  { apiKey: "secret123" },
  { persistKey: "sensitive", encrypted: true }
);

The state will be securely stored in localStorage and automatically decrypted on load.

🔄 Cross-Tab Synchronization

When using persistent signals, react-unprop automatically synchronizes your state across browser tabs. Any changes made in one tab are instantly reflected in others with no additional configuration.

Example

// In any tab
const userSignal = createSignal({ name: "Alice" }, { persistKey: "user" });

// In one tab, update the signal
userSignal.set({ name: "Bob" });

// In another tab, the signal will automatically update
// and components using useSignal(userSignal) will re-render

This works for both encrypted and unencrypted signals.

🖥️ Server-Side Rendering Support

react-unprop is fully compatible with Next.js and other SSR frameworks. The library automatically detects server environments and handles them appropriately.

Usage with Next.js

// shared/signals.js
import { createSignal } from "react-unprop";

// Works in both client and server environments
export const userSignal = createSignal({ name: "Guest" });

// pages/index.js
import { useSignal } from "react-unprop";
import { userSignal } from "../shared/signals";

export default function Home() {
  // Works with SSR and hydration
  const user = useSignal(userSignal);

  return <div>Hello, {user.name}!</div>;
}

Storage-based features (persistence, cross-tab sync) automatically work only in the browser, with no special configuration needed.

🧠 API Reference

createSignal(initialValue, options?)

Creates a reactive signal.

Parameters:

  • initialValue: T — The initial value of the signal
  • options.persistKey?: string — Key for localStorage persistence and cross-tab sync
  • options.encrypted?: boolean — Whether to encrypt persisted data (default: false)
  • options.secret?: string — Custom encryption secret (optional)

Returns an object with:

  • get(): T — returns current value
  • set(newValue | updaterFn): void — sets value or updates with function
  • subscribe(callback): () => void — used internally by useSignal

useSignal(signal)

React hook that subscribes to a signal and auto-rerenders on update.

Parameters:

  • signal: Signal<T> — The signal to subscribe to

Returns: The current value of the signal.

🧪 Code Examples

🔢 Counter

const counter = createSignal(0);

function App() {
  const count = useSignal(counter);

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => counter.set((c) => c + 1)}>+</button>
      <button onClick={() => counter.set((c) => c - 1)}>-</button>
    </div>
  );
}

👥 Shared User State

const userSignal = createSignal({ name: "John", age: 25 }, { persist: true });

function Profile() {
  const user = useSignal(userSignal);
  return (
    <div>
      {user.name}, {user.age}
    </div>
  );
}

📋 Dynamic List (Array)

const itemsSignal = createSignal([]);

function AddItem() {
  return (
    <button
      onClick={() =>
        itemsSignal.set((prev) => [...prev, `Item ${prev.length + 1}`])
      }
    >
      Add Item
    </button>
  );
}

function ItemList() {
  const items = useSignal(itemsSignal);
  return (
    <ul>
      {items.map((item, idx) => (
        <li key={idx}>{item}</li>
      ))}
    </ul>
  );
}

🔄 Toggle State (Boolean)

const darkModeSignal = createSignal(false);

function ToggleDarkMode() {
  const darkMode = useSignal(darkModeSignal);
  return (
    <button onClick={() => darkModeSignal.set(!darkMode)}>
      {darkMode ? "Disable" : "Enable"} Dark Mode
    </button>
  );
}

📝 Form Input Binding

const formSignal = createSignal({ name: "", email: "" });

function Form() {
  const form = useSignal(formSignal);
  return (
    <form>
      <input
        value={form.name}
        onChange={(e) =>
          formSignal.set((f) => ({ ...f, name: e.target.value }))
        }
        placeholder="Name"
      />
      <input
        value={form.email}
        onChange={(e) =>
          formSignal.set((f) => ({ ...f, email: e.target.value }))
        }
        placeholder="Email"
      />
    </form>
  );
}

🔄 Migration Guide

From useState

// Before
const [count, setCount] = useState(0);

// After
const countSignal = createSignal(0);
const count = useSignal(countSignal);

From Context

// Before: Context
const ThemeContext = createContext();

// After: Signal
const themeSignal = createSignal("light");

🌟 Why Open Source?

  • 👐 Built with collaboration in mind
  • 🛠️ Customizable and extensible
  • 🌱 Actively maintained and open to PRs
  • 📣 Community-driven roadmap
  • 🧩 Transparent and secure codebase

🤝 Contributing

We welcome all contributions! Whether it's improving docs, fixing bugs, or adding features — you're welcome to join the journey.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.


Made with ❤️ by Ayush Chauhan and the open-source community

Report Bug · Request Feature · Documentation