{"id":41764776,"url":"https://github.com/bonniss/react-easy-provider","last_synced_at":"2026-01-25T02:30:50.268Z","repository":{"id":329512300,"uuid":"1119059283","full_name":"bonniss/react-easy-provider","owner":"bonniss","description":"React Provider without Context boilerplate","archived":false,"fork":false,"pushed_at":"2025-12-20T03:34:06.000Z","size":17,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-22T12:17:32.682Z","etag":null,"topics":["react","react-context","react-provider","react-utils"],"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/bonniss.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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-12-18T17:18:03.000Z","updated_at":"2025-12-20T03:34:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bonniss/react-easy-provider","commit_stats":null,"previous_names":["bonniss/react-easy-provider"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/bonniss/react-easy-provider","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bonniss%2Freact-easy-provider","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bonniss%2Freact-easy-provider/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bonniss%2Freact-easy-provider/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bonniss%2Freact-easy-provider/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bonniss","download_url":"https://codeload.github.com/bonniss/react-easy-provider/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bonniss%2Freact-easy-provider/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28742469,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-25T01:40:51.112Z","status":"online","status_checked_at":"2026-01-25T02:00:06.841Z","response_time":113,"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":["react","react-context","react-provider","react-utils"],"created_at":"2026-01-25T02:30:50.063Z","updated_at":"2026-01-25T02:30:50.252Z","avatar_url":"https://github.com/bonniss.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# react-easy-provider\n\n\u003e A utility to create React Provider without Context boilerplate\n\n## The problem\n\nSuppose you want to create a reusable counter to avoid prop drilling, you end up going through several mechanical steps.\n\n**Step 1** — Define the value shape\n\n```tsx\ntype CounterContextValue = {\n  count: number\n  inc: () =\u003e void\n}\n```\n\n**Step 2** — Create the Context instance\n\n```tsx\nconst CounterContext = createContext\u003cCounterContextValue | undefined\u003e(undefined)\n```\n\n**Step 3** — Build the Provider\n\n```tsx\nexport const CounterProvider = ({ children }: { children: ReactNode }) =\u003e {\n  const [count, setCount] = useState(0)\n\n  return (\n    \u003cCounterContext.Provider\n      value={{ count, inc: () =\u003e setCount((c) =\u003e c + 1) }}\n    \u003e\n      {children}\n    \u003c/CounterContext.Provider\u003e\n  )\n}\n```\n\n**Step 4** — Expose a consumer hook (with a optional runtime guard)\n\n```tsx\nexport const useCounter = () =\u003e {\n  const ctx = useContext(CounterContext)\n  if (!ctx) throw new Error(\"useCounter must be used within CounterProvider\")\n  return ctx\n}\n```\n\nA few things become noticeable:\n\n- To keep things maintainable, you usually end up splitting this into multiple files: types, Context, the Provider, and the consumer hook.\n- When the logic grows, you have to update the types along with it.\n- Context often feels redundant — you declare it once and rarely touch it again.\n\nRepeating this clunky pattern enough times led to this package.\n\n## Getting started\n\n### Installation\n\n```sh\nnpm install react-easy-provider\n\nyarn add react-easy-provider\n\npnpm add react-easy-provider\n```\n\n### Creating a Provider\n\nUse `createProvider` by defining your shared logic once. This function becomes the single source of truth for both the logic and its types — **without manually creating or consuming React Context**.\n\n```tsx\nimport { useState } from \"react\"\nimport { createProvider } from \"react-easy-provider\"\n\nexport const [useCounter, CounterProvider] = createProvider(\n  // Function that defines the shared logic and returns the context value\n  (initial?: number) =\u003e {\n    const [count, setCount] = useState(initial ?? 0)\n    return {\n      count,\n      inc: () =\u003e setCount((c) =\u003e c + 1),\n    }\n  },\n  // Optional display name for the Provider, useful for debugging\n  \"CounterProvider\"\n)\n```\n\nNow you have both the hook and the Provider — just wire them up like usual.\n\n```tsx\n// Wrap your app (or part of it)\n\u003cCounterProvider defaultValue={10}\u003e\n  \u003cApp /\u003e\n\u003c/CounterProvider\u003e\n\n// Use the shared logic anywhere below the Provider\nconst { count, inc } = useCounter();\n\nreturn (\n  \u003cbutton onClick={inc}\u003e\n    Count: {count}\n  \u003c/button\u003e\n);\n```\n\n### Error when the hook is used outside a Provider\n\nUsing the example `useCounter` hook without a corresponding Provider above it in the component tree:\n\n```tsx\nconst Counter = () =\u003e {\n  const { count } = useCounter();\n  return \u003cdiv\u003e{count}\u003c/div\u003e;\n};\n```\n\nwill result in the following error being thrown by default:\n\n```html\nError: useContextValue must be used within CounterProvider\n```\n\nThis behavior is intentional to help catch misconfiguration early. The error message includes the Provider name to make debugging easier, so giving your Provider a clear and explicit name is strongly recommended. A well-named Provider makes it immediately obvious which wrapper is missing and where the hook is expected to be used, especially in apps with multiple Providers.\n\n### Fail quietly\n\nYou can allow to fail quietly when a Provider might not always be present, or when you want a safe default.\n\n```tsx\nconst { count, inc } = useCounter({\n  shouldFailQuietly: true,\n  fallback: {\n    count: 0,\n    inc: () =\u003e {},\n  },\n});\n```\n\n### Standalone (isolated) mode\n\nThere are cases where you want to reuse the shared logic directly without setting up a Provider, and without duplicating the hook just to support both patterns. For these scenarios, you can enable standalone mode.\n\nWhen standalone mode is enabled, the hook runs fully independently, ignoring any Provider even if one exists in the component tree. This allows you to reuse the same logic without creating or wrapping a Provider, which is especially useful for isolated usage such as tests or component previews.\n\n```tsx\nconst { count, inc } = useCounter({\n  standalone: true,\n  defaultValue: 5,\n});\n```\n\n### I still want direct access to Context\n\nFine! `createProvider` returns the underlying Context as the third value.\n\n```tsx\nconst [useCounter, CounterProvider, CounterContext] = createProvider(\n  useCounterLogic,\n  \"CounterProvider\"\n);\n```\n\n## API\n\n### `createProvider\u003cT, U\u003e(fn, displayName?)`\n\n```ts\nconst [useValue, Provider, Context] = createProvider(fn, displayName);\n```\n\n* **`fn: (defaultValue?: U) =\u003e T`**\n  Defines the shared logic.\n  The return type `T` becomes the Context value type.\n\n* **`displayName?: string`**\n  Optional Provider display name.\n\n**Returns:**\n\n* **`useValue: (options?) =\u003e T`**\n* **`Provider: React.FC\u003c{ defaultValue?: U }\u003e`**\n* **`Context: React.Context\u003cT | undefined\u003e`**\n\n### `useValue(options?)`\n\n```ts\nconst value = useValue(options);\n```\n\n```ts\ntype Options\u003cT, U\u003e = {\n  standalone?: boolean;\n  defaultValue?: U;\n  shouldFailQuietly?: boolean;\n  fallback?: T;\n};\n```\n\n* Inside a Provider → returns Context value\n* Outside a Provider:\n\n  * throws by default\n  * returns `fallback` if `shouldFailQuietly`\n  * runs `fn(defaultValue)` if `standalone`\n\n### Type inference\n\nAll types are inferred from `fn`.\nNo manual Context value types required.\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbonniss%2Freact-easy-provider","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbonniss%2Freact-easy-provider","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbonniss%2Freact-easy-provider/lists"}