
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
react-cosmic
Advanced tools
React state that persists across refreshes, syncs across tabs, and survives crashes
React state that persists across refreshes, syncs across tabs, and survives crashes. No server required.
const [draft, setDraft] = useOrbit('email-draft', '');
// That's it. Survives refresh. Syncs across tabs. Zero config.
React Cosmic gives you hooks that work like useState but persist to IndexedDB and sync across tabs automatically. Built on Yjs CRDTs, so conflicts resolve themselves — even with real-time collaboration.
bun add react-cosmic # or npm/pnpm/yarn
Wrap your app:
import { OrbitProvider } from 'react-cosmic';
function App() {
return (
<OrbitProvider storeId="my-app">
<YourStuff />
</OrbitProvider>
);
}
Use the hooks:
import {
useOrbit,
useOrbitText,
useOrbitObject,
useOrbitArray,
useOrbitUndoManager,
useOrbitStatus,
useOrbitCircuit,
useOrbitAwareness,
useSetLocalAwareness
} from 'react-cosmic';
function Form() {
const [email, setEmail] = useOrbit('email', '');
const [bio, setBio] = useOrbitText('bio', '');
const [profile, updateProfile] = useOrbitObject('profile', {
name: '',
age: 0
});
return (
<form>
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<textarea
value={bio}
onChange={(e) => setBio(e.target.value)}
/>
<input
value={profile.name}
onChange={(e) => updateProfile({ name: e.target.value })}
/>
</form>
);
}
That's it. Now open two tabs and watch them sync. Close the browser and reopen it. Your data is still there.
<OrbitProvider>Put this around your app or component tree.
<OrbitProvider
storeId="unique-id" // required - same ID = shared state
enableStorage={true} // optional - persist to IndexedDB (default: true)
enableTabSync={true} // optional - sync across tabs (default: true)
persistDebounceMs={300} // optional - debounce saves (default: 300)
websocketUrl="ws://localhost:1234" // optional - sync across devices/users
websocketOptions={{ // optional - WebSocket config
retryDelay: 3000, // reconnect delay in ms
protocols: ["my-protocol"] // WebSocket protocols
}}
>
useOrbit(key, initialValue)Like useState but it remembers. Use for primitives and simple values.
const [count, setCount] = useOrbit('count', 0);
const [name, setName] = useOrbit('name', '');
const [enabled, setEnabled] = useOrbit('enabled', false);
useOrbitText(key, initialValue?)For text fields. Uses Yjs Text internally for proper collaborative editing.
const [content, setContent] = useOrbitText('doc-content', '');
useOrbitObject(key, initialValue)For nested objects. Lets you update individual properties without replacing the whole thing.
const [user, updateUser] = useOrbitObject('user', {
name: '',
email: '',
age: 0
});
updateUser({ age: 25 }); // only updates age, keeps name and email
useOrbitArray<T>(key, initialValue?)For ordered lists. Concurrent inserts and deletes from multiple clients merge correctly.
const [todos, { push, insert, remove, clear }] = useOrbitArray<string>('todos', []);
push('Buy milk'); // append
insert(0, 'First item'); // insert at index
remove(1); // remove at index
clear(); // remove all
useOrbitUndoManager(key, scope?)Undo/redo scoped to a single Orbit key. Operations within 500ms are grouped into one step.
const [content, setContent] = useOrbitText('doc', '');
const { undo, redo, canUndo, canRedo } = useOrbitUndoManager('doc', 'text');
// scope: 'text' (default) | 'map' | 'array'
useOrbitStatus()Track WebSocket connection status. Returns 'connected', 'connecting', or 'disconnected'.
const status = useOrbitStatus();
return <div>Status: {status}</div>;
Only useful when websocketUrl is configured. Returns 'disconnected' if WebSockets aren't enabled.
useOrbitAwareness<T>()Read presence data for all connected clients. Returns a Map<number, T> where the key is the client ID and the value is their awareness state.
interface UserPresence {
name: string;
color: string;
cursor?: number;
}
const users = useOrbitAwareness<UserPresence>();
return (
<div>
{Array.from(users.values()).map(user => (
<div key={user.name}>{user.name}</div>
))}
</div>
);
Only works when websocketUrl is configured.
useOrbitCircuit()Returns true when the WebSocket circuit breaker has tripped (stopped reconnecting after repeated failures). The app continues to work with local + tab sync.
const circuitOpen = useOrbitCircuit();
if (circuitOpen) {
return <Banner>Working offline — server unreachable</Banner>;
}
useSetLocalAwareness<T>(state)Broadcast your presence state to all connected clients. Automatically cleans up on unmount.
interface UserPresence {
name: string;
color: string;
}
function MyComponent() {
const [profile] = useOrbit<UserPresence>('profile', {
name: 'Alice',
color: '#ff0000'
});
useSetLocalAwareness(profile);
return <div>Your name: {profile.name}</div>;
}
Only works when websocketUrl is configured.
Basic form demo (local persistence and tab sync):
git clone https://github.com/JR-G/react-cosmic
cd react-cosmic
bun install
bun run demo
Open multiple tabs to see cross-tab sync in action.
Collaboration demo (real-time multi-user sync with presence):
Terminal 1 - start the WebSocket server:
bun run demo:server
Terminal 2 - start the demo:
bun run demo:collab
Open multiple browsers to see real-time collaboration with presence indicators and remote cursors.
Not for:
localStorage / zustand persist / jotai storage? They don't handle cross-tab sync properly. Open two tabs, edit in both, and one overwrites the other. React Cosmic uses CRDTs — edits merge automatically, no data loss.
Plain Yjs?
You can, but you'll write a lot of boilerplate. React Cosmic gives you useState-like hooks with persistence and sync built in.
A backend? Sometimes you don't need one. Drafts, preferences, and form state can live entirely in the browser. When you do need sync, just add a WebSocket URL — same API, now multiplayer.
Want to sync across devices or add real-time multiplayer? Run a WebSocket server. The same CRDT that syncs your tabs now syncs across devices and users, with automatic conflict resolution.
The simplest option is using bunx:
bunx y-websocket-server --port 1234
Or install globally:
bun install -g y-websocket
y-websocket-server --port 1234
Then connect your app:
<OrbitProvider
storeId="my-app"
websocketUrl="ws://localhost:1234"
>
<YourStuff />
</OrbitProvider>
Now users on different devices see each other's changes in real-time. The CRDT handles all conflict resolution automatically - whether it's two tabs, two users, or twenty.
For production, you'll want auth, persistence, and proper scaling. Check out y-websocket docs, PartyKit, or other hosted Yjs providers.
MIT
FAQs
React state that persists across refreshes, syncs across tabs, and survives crashes
The npm package react-cosmic receives a total of 7 weekly downloads. As such, react-cosmic popularity was classified as not popular.
We found that react-cosmic demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.