{"id":15414870,"url":"https://github.com/quiibz/particule","last_synced_at":"2025-04-19T12:48:17.131Z","repository":{"id":45269060,"uuid":"405122732","full_name":"QuiiBz/particule","owner":"QuiiBz","description":"Fine-grained atomic React state management library ","archived":false,"fork":false,"pushed_at":"2021-12-26T13:41:47.000Z","size":578,"stargazers_count":33,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-19T11:54:58.031Z","etag":null,"topics":["atom","dispatch","react","recoil","redux","state","state-management"],"latest_commit_sha":null,"homepage":"https://particule.vercel.app/","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/QuiiBz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-09-10T15:14:05.000Z","updated_at":"2025-04-15T15:07:16.000Z","dependencies_parsed_at":"2022-08-19T03:20:34.494Z","dependency_job_id":null,"html_url":"https://github.com/QuiiBz/particule","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/QuiiBz%2Fparticule","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/QuiiBz%2Fparticule/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/QuiiBz%2Fparticule/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/QuiiBz%2Fparticule/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/QuiiBz","download_url":"https://codeload.github.com/QuiiBz/particule/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249698747,"owners_count":21312261,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["atom","dispatch","react","recoil","redux","state","state-management"],"created_at":"2024-10-01T17:05:06.667Z","updated_at":"2025-04-19T12:48:17.115Z","avatar_url":"https://github.com/QuiiBz.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003ch1 align=\"center\"\u003eparticule\u003c/h1\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/QuiiBz/particule/actions\"\u003e\n        \u003cimg src=\"https://github.com/QuiiBz/particule/workflows/CI/badge.svg\" /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/QuiiBz/particule/blob/main/LICENSE\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Licence-MIT-blue\" /\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#fine-grained\"\u003eFine-grained\u003c/a\u003e \u003cb\u003eatomic\u003c/b\u003e React state management library\n  \u003cbr /\u003e\n  \u003cbr /\u003e\n  \u003ccode\u003eyarn add particule\u003c/code\u003e\n\u003c/p\u003e\n\n---\n\n- [✨ Features](#-features)\n- [🚀 Examples](#-examples)\n  - [Basic](#basic)\n  - [Fine-grained](#fine-grained)\n  - [Composition](#composition)\n  - [Suspense](#suspense)\n  - [Dispatch](#dispatch)\n  - [Custom `atom` with hooks](#custom-atom-with-hooks)\n- [📚 Documentation](#-documentation)\n- [License](#license)\n\n**Particule** is an atomic React state management library inspired by the best of [Recoil](https://recoiljs.org/), [Jotai](https://jotai.pmnd.rs/) and [Redux](https://redux.js.org/). You can choose **which component subscribe to which state** and so **avoid useless re-render and computations**.\n\n## ✨ Features\n\n- **Super-easy** API\n- **TypeScript** ready\n- **Suspense** support\n- **Minimal** footprint \u003csub\u003e(1kB gzipped)\u003c/sub\u003e\n- **Hooks** to add functionality\n\n## 🚀 Examples\n\n### Basic\n\n\u003ca href=\"https://codesandbox.io/s/basic-example-particule-l79zc?file=/src/App.tsx\" target=\"_blank\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/code-sandbox-black\" /\u003e\n\u003c/a\u003e\n\n```tsx\nconst textAtom = atom('Hello world!')\n\nfunction App() {\n  const [text, setText] = useAtom(textAtom)\n\n  return (\n    \u003c\u003e\n      \u003cp\u003e{text}\u003c/p\u003e\n      \u003cbutton onClick={() =\u003e setText('Updated!')}\u003eUpdate\u003c/button\u003e\n    \u003c/\u003e\n  )\n}\n```\n\n### Fine-grained\n\n\u003ca href=\"https://codesandbox.io/s/fine-grained-example-particule-lxnse?file=/src/App.tsx:0-453\" target=\"_blank\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/code-sandbox-black\" /\u003e\n\u003c/a\u003e\n\n```tsx\nconst textAtom = atom('Hello world!')\n\nfunction Text() {\n  const text = useGetAtom(textAtom)\n\n  return \u003cp\u003e{text}\u003c/p\u003e\n}\n\n// Won't re-render!\nfunction Button() {\n  const setText = useSetAtom(textAtom)\n\n  return \u003cbutton onClick={() =\u003e setText('Updated!')}\u003eUpdate\u003c/button\u003e\n}\n\n// Won't re-render!\nfunction App() {\n  return (\n    \u003c\u003e\n      \u003cText /\u003e\n      \u003cButton /\u003e\n    \u003c/\u003e\n  )\n}\n```\n\n### Composition\n\n\u003ca href=\"https://codesandbox.io/s/composition-example-particule-7ln35?file=/src/App.tsx\" target=\"_blank\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/code-sandbox-black\" /\u003e\n\u003c/a\u003e\n\n```tsx\nconst eurosAtom = atom(10)\nconst dollarsAtom = atom(get =\u003e get(eurosAtom) * 1.15)\n\nfunction App() {\n  const [euros, setEuros] = useAtom(eurosAtom)\n  const [dollars, setDollars] = useAtom(dollarsAtom)\n\n  return (\n    \u003c\u003e\n      \u003cinput onChange={({ target }) =\u003e setEuros(target.value)} value={euros} /\u003e\n      \u003cinput onChange={({ target }) =\u003e setDollars(target.value)} value={dollars} /\u003e\n    \u003c/\u003e\n  )\n}\n```\n\n### Suspense\n\n\u003ca href=\"https://codesandbox.io/s/suspense-example-particule-w5gj1?file=/src/App.tsx\" target=\"_blank\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/code-sandbox-black\" /\u003e\n\u003c/a\u003e\n\n```tsx\nconst nameAtom = atom(async () =\u003e {\n  const json = await (await fetch(\"https://randomuser.me/api/\")).json();\n\n  return json.results[0].name.first;\n});\n\nfunction Name() {\n  const name = useGetAtom(nameAtom)\n\n  return \u003cp\u003eMy name is {name}\u003c/p\u003e\n}\n\nfunction App() {\n  return (\n    \u003cSuspense fallback='Loading...'\u003e\n      \u003cName /\u003e\n    \u003c/Suspense\u003e\n  )\n}\n```\n\n### Dispatch\n\n\u003ca href=\"https://codesandbox.io/s/dispatch-particule-0p60u?file=/src/App.tsx\" target=\"_blank\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/code-sandbox-black\" /\u003e\n\u003c/a\u003e\n\n```tsx\nconst counterAtom = atom(0)\nconst dispatchCounter = dispatch(counterAtom, value =\u003e ({\n  increment: (newValue: number) =\u003e value + newValue,\n  decrement: (newValue: number) =\u003e value - newValue,\n}))\n\nfunction App() {\n  const counter = useGetAtom(counterAtom)\n\n  return (\n    \u003c\u003e\n      \u003cp\u003e{counter}\u003c/p\u003e\n      \u003cbutton onClick={() =\u003e dispatchCounter('increment', 1)}\u003eIncrement\u003c/button\u003e\n      \u003cbutton onClick={() =\u003e dispatchCounter('decrement', 1)}\u003eDecrement\u003c/button\u003e\n    \u003c/\u003e\n  )\n}\n```\n\n### Custom `atom` with hooks\n\n\u003ca href=\"https://codesandbox.io/s/custom-atom-with-hooks-example-particule-yifif?file=/src/App.tsx\" target=\"_blank\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/code-sandbox-black\" /\u003e\n\u003c/a\u003e\n\n```tsx\nconst noZeroAtom = createAtom({\n  beforeValueSet: (_, value) =\u003e {\n    if (value === 0) {\n      throw new Error('Cannot set value to 0')\n    }\n\n    return value\n  }\n})\n\nconst counterAtom = noZeroAtom(3)\n\nfunction App() {\n  const [count, setCount] = useAtom(counterAtom)\n\n  return (\n    \u003c\u003e\n      \u003cp\u003e{count}\u003c/p\u003e\n      \u003cbutton onClick={() =\u003e setCount(count =\u003e count - 1)}\u003eReduce\u003c/button\u003e\n    \u003c/\u003e\n  )\n}\n```\n\n## 📚 Documentation\n\nSee the website at [particule.vercel.app](https://particule.vercel.app/).\n\u003csub\u003eHosted on [Vercel](https://vercel.com/).\u003c/sub\u003e\n\n## License\n\n[MIT](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquiibz%2Fparticule","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fquiibz%2Fparticule","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquiibz%2Fparticule/lists"}