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.
Maintainers
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.
✨ 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-unpropor using yarn:
yarn add react-unpropor 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-renderThis 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 signaloptions.persistKey?: string— Key for localStorage persistence and cross-tab syncoptions.encrypted?: boolean— Whether to encrypt persisted data (default: false)options.secret?: string— Custom encryption secret (optional)
Returns an object with:
get(): T— returns current valueset(newValue | updaterFn): void— sets value or updates with functionsubscribe(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.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - 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
