{"id":51203932,"url":"https://github.com/stackoverprof/use-shared-state","last_synced_at":"2026-06-28T02:06:56.789Z","repository":{"id":310422829,"uuid":"1039807829","full_name":"stackoverprof/use-shared-state","owner":"stackoverprof","description":"[NPM] Sharing react state made simple","archived":false,"fork":false,"pushed_at":"2026-02-03T03:54:07.000Z","size":121,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-03T17:40:20.646Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stackoverprof.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-18T02:31:07.000Z","updated_at":"2026-02-03T03:54:10.000Z","dependencies_parsed_at":"2025-08-18T02:42:53.038Z","dependency_job_id":"ef913441-fd40-4fe0-bf83-0ea728998c8c","html_url":"https://github.com/stackoverprof/use-shared-state","commit_stats":null,"previous_names":["stackoverprof/use-shared-state"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/stackoverprof/use-shared-state","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackoverprof%2Fuse-shared-state","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackoverprof%2Fuse-shared-state/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackoverprof%2Fuse-shared-state/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackoverprof%2Fuse-shared-state/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stackoverprof","download_url":"https://codeload.github.com/stackoverprof/use-shared-state/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackoverprof%2Fuse-shared-state/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34874571,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-28T02:00:05.809Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-28T02:06:56.013Z","updated_at":"2026-06-28T02:06:56.780Z","avatar_url":"https://github.com/stackoverprof.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# use-shared-state\n\n[![npm version](https://badge.fury.io/js/%40stackoverprof%2Fuse-shared-state.svg)](https://badge.fury.io/js/%40stackoverprof%2Fuse-shared-state)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)\n\nA lightweight React hook for sharing state across components with optional localStorage persistence and cross-tab synchronization.\n\n## 🚀 Live Demo\n\n**[Try it live →](https://use-shared-state-demo.vercel.app/)**\n\nSee real-time state sharing, persistence, and cross-tab synchronization in action!\n\n## Features\n\n-   🚀 **Simple API** - Drop-in replacement for `useState` with cross-component sharing\n-   💾 **Optional Persistence** - Use `@` prefix for localStorage persistence\n-   🔄 **Cross-tab Sync** - Automatic synchronization across browser tabs\n-   ⚡ **High Performance** - Optimized with minimal overhead using Map storage\n-   🛡️ **Type Safe** - Full TypeScript support with generics\n-   🎯 **Lite SWR** - Built with custom lightweight SWR implementation (~100 lines)\n-   🧪 **Zero Dependencies** - No external dependencies except React\n\n## Installation\n\n```bash\n# Install the library\nnpm install @stackoverprof/use-shared-state\n```\n\n\u003e **Note:** React \u003e=16.8.0 is required (peer dependency)\n\n## Quick Start\n\n```tsx\nimport useSharedState from \"@stackoverprof/use-shared-state\";\n\n// Basic shared state (memory only)\nconst [count, setCount] = useSharedState(\"counter\", 0);\n\n// Persistent shared state (localStorage + cross-tab sync)\nconst [user, setUser] = useSharedState(\"@user\", { name: \"John\" });\n// ↳ Saved in localStorage as \"shared@user\"\n```\n\n## API Reference\n\n### `useSharedState\u003cT\u003e(key: string, initialValue?: T)`\n\nReturns a tuple `[state, setState]` similar to React's `useState`.\n\n#### Parameters\n\n-   `key` - Unique identifier for the shared state\n    -   Regular keys: Memory-only storage\n    -   Keys with `@` prefix: Persistent localStorage + cross-tab sync\n-   `initialValue` - Default value when state is undefined\n\n#### Returns\n\n-   `state` - Current state value (T | undefined)\n-   `setState` - Function to update state, supports value or updater function\n\n## Performance\n\n-   **Memory-only keys**: ~0.1ms overhead\n-   **Persistent keys**: ~2-3ms overhead (includes localStorage operations)\n-   **Cross-tab sync**: Automatic with StorageEvent API\n-   **Memory usage**: Efficient Map-based storage with automatic cleanup\n-   **Re-renders**: Only components using the changed state key re-render\n\n## Re-rendering Behavior\n\n**Important:** Only components that actively use a shared state key will re-render when that state changes.\n\n✅ **Precise targeting**: Only components using the changed key re-render  \n✅ **Parent isolation**: Parent won't re-render unless it uses shared state  \n✅ **Sibling isolation**: Unrelated siblings won't re-render  \n✅ **Performance**: Better than Context (which can cause cascade re-renders)\n\n## Comparison with Alternatives\n\n| Feature              | use-shared-state | Redux  | Context | localStorage |\n| -------------------- | ---------------- | ------ | ------- | ------------ |\n| Setup complexity     | Minimal          | High   | Medium  | Manual       |\n| TypeScript support   | Full             | Good   | Good    | Manual       |\n| Cross-component sync | ✅               | ✅     | ✅      | ❌           |\n| Persistence          | Optional         | Manual | ❌      | Manual       |\n| Cross-tab sync       | ✅               | Manual | ❌      | Manual       |\n| Performance          | High             | Medium | Low\\*   | High         |\n| Bundle size          | Small            | Large  | None    | None         |\n\n\\*Context can cause unnecessary re-renders\n\n## Best Practices\n\n1. **Use regular keys for temporary state**\n\n    ```tsx\n    const [loading, setLoading] = useSharedState(\"loading\", false);\n    ```\n\n2. **Use @ prefix for data that should persist**\n\n    ```tsx\n    const [settings, setSettings] = useSharedState(\"@user-settings\", {});\n    ```\n\n3. **Provide default values for better TypeScript inference**\n\n    ```tsx\n    const [items, setItems] = useSharedState\u003cItem[]\u003e(\"items\", []);\n    ```\n\n4. **Use updater functions for complex state changes**\n    ```tsx\n    setCart((prev) =\u003e ({ ...prev, total: calculateTotal(prev.items) }));\n    ```\n\n## Cleanup \u0026 Memory Management\n\n### **Automatic Cleanup**\n\n-   ✅ **Lite SWR reference counting** - Cleans up when ALL components using a key unmount\n-   ✅ **Event listeners removed** - Cross-tab sync listeners auto-cleanup\n-   ✅ **Memory efficient** - Map-based storage with garbage collection\n\n### **What Gets Cleaned Up**\n\n| Type           | Lite SWR Cleanup      | localStorage Cleanup         |\n| -------------- | --------------------- | ---------------------------- |\n| `\"user-data\"`  | ✅ Auto (memory only) | ❌ N/A                       |\n| `\"@user-data\"` | ✅ Memory cache only  | ❌ Stays until manual delete |\n\n### **Manual Cleanup**\n\n```tsx\nimport { sharedStateUtils } from \"@stackoverprof/use-shared-state\";\n\n// Clear specific keys\nsharedStateUtils.delete(\"temp-data\"); // Memory only\nsharedStateUtils.delete(\"@user-session\"); // Memory + localStorage\n\n// Clear all (with/without persistent)\nsharedStateUtils.clear(false); // Memory only\nsharedStateUtils.clear(true); // Memory + localStorage\n\n// Route cleanup\nuseEffect(\n    () =\u003e () =\u003e {\n        sharedStateUtils.delete(\"dashboard-filters\");\n    },\n    []\n);\n```\n\n## Utility Functions\n\nThe library provides debugging utilities via `sharedStateUtils`:\n\n```tsx\nimport { sharedStateUtils } from \"@stackoverprof/use-shared-state\";\n\n// Get all current keys\nconsole.log(sharedStateUtils.getKeys());\n\n// Get current state size\nconsole.log(sharedStateUtils.getSize());\n\n// Clear all state (optionally including persistent)\nsharedStateUtils.clear(true);\n\n// Delete specific key\nsharedStateUtils.delete(\"some-key\");\n\n// Get all persistent keys\nconsole.log(sharedStateUtils.getPersistentKeys());\n```\n\n## Requirements\n\n-   React \u003e= 16.8.0\n\n## Examples\n\n### Basic Counter\n\n```tsx\nimport useSharedState from \"@stackoverprof/use-shared-state\";\n\nfunction Counter() {\n    const [count, setCount] = useSharedState(\"counter\", 0);\n\n    return (\n        \u003cdiv\u003e\n            \u003cp\u003eCount: {count}\u003c/p\u003e\n            \u003cbutton onClick={() =\u003e setCount(count + 1)}\u003eIncrement\u003c/button\u003e\n        \u003c/div\u003e\n    );\n}\n```\n\n### Shopping Cart with Persistence\n\n```tsx\ninterface CartItem {\n    id: string;\n    name: string;\n    price: number;\n    quantity: number;\n}\n\nfunction ProductList() {\n    const [cartItems, setCartItems] = useSharedState\u003cCartItem[]\u003e(\n        \"@cart-items\",\n        []\n    );\n\n    const addToCart = (product: CartItem) =\u003e {\n        setCartItems((prev) =\u003e {\n            const existing = prev?.find((item) =\u003e item.id === product.id);\n            if (existing) {\n                return (\n                    prev?.map((item) =\u003e\n                        item.id === product.id\n                            ? { ...item, quantity: item.quantity + 1 }\n                            : item\n                    ) || []\n                );\n            }\n            return [...(prev || []), { ...product, quantity: 1 }];\n        });\n    };\n\n    return \u003cdiv\u003e{/* Product list */}\u003c/div\u003e;\n}\n\nfunction CartSummary() {\n    const [cartItems] = useSharedState\u003cCartItem[]\u003e(\"@cart-items\", []);\n\n    const total =\n        cartItems?.reduce((sum, item) =\u003e sum + item.price * item.quantity, 0) ||\n        0;\n\n    return (\n        \u003cdiv\u003e\n            \u003ch3\u003eCart ({cartItems?.length || 0} items)\u003c/h3\u003e\n            \u003cp\u003eTotal: ${total.toFixed(2)}\u003c/p\u003e\n        \u003c/div\u003e\n    );\n}\n```\n\n### Cross-Component Form State\n\n```tsx\ninterface FormData {\n    name: string;\n    email: string;\n    preferences: string[];\n}\n\nfunction Step1() {\n    const [formData, setFormData] = useSharedState\u003cFormData\u003e(\"@form-data\", {\n        name: \"\",\n        email: \"\",\n        preferences: [],\n    });\n\n    return (\n        \u003cdiv\u003e\n            \u003cinput\n                value={formData?.name || \"\"}\n                onChange={(e) =\u003e\n                    setFormData((prev) =\u003e ({\n                        ...prev!,\n                        name: e.target.value,\n                    }))\n                }\n                placeholder=\"Name\"\n            /\u003e\n        \u003c/div\u003e\n    );\n}\n\nfunction Step2() {\n    const [formData, setFormData] = useSharedState\u003cFormData\u003e(\"@form-data\");\n\n    return (\n        \u003cdiv\u003e\n            \u003cp\u003eHello, {formData?.name}!\u003c/p\u003e\n            \u003cinput\n                value={formData?.email || \"\"}\n                onChange={(e) =\u003e\n                    setFormData((prev) =\u003e ({\n                        ...prev!,\n                        email: e.target.value,\n                    }))\n                }\n                placeholder=\"Email\"\n            /\u003e\n        \u003c/div\u003e\n    );\n}\n```\n\n## License\n\nMIT\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstackoverprof%2Fuse-shared-state","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstackoverprof%2Fuse-shared-state","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstackoverprof%2Fuse-shared-state/lists"}