{"id":17855621,"url":"https://github.com/nebrius/recoil-bootstrap","last_synced_at":"2026-01-21T03:01:46.388Z","repository":{"id":175025978,"uuid":"652879443","full_name":"nebrius/recoil-bootstrap","owner":"nebrius","description":"Recoil Bootstrap enables apps to initialize Recoil with runtime bootstrap data in multi-page applications, such as in Next.js.","archived":false,"fork":false,"pushed_at":"2023-10-10T21:01:54.000Z","size":514,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-24T08:18:29.843Z","etag":null,"topics":["next","nextjs","nextjs13","recoil","recoiljs"],"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/nebrius.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}},"created_at":"2023-06-13T01:33:06.000Z","updated_at":"2023-10-09T15:16:19.000Z","dependencies_parsed_at":"2024-10-28T03:23:01.779Z","dependency_job_id":"7bede5a7-3462-43b1-ab27-667458344204","html_url":"https://github.com/nebrius/recoil-bootstrap","commit_stats":null,"previous_names":["nebrius/recoil-bootstrap"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/nebrius/recoil-bootstrap","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nebrius%2Frecoil-bootstrap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nebrius%2Frecoil-bootstrap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nebrius%2Frecoil-bootstrap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nebrius%2Frecoil-bootstrap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nebrius","download_url":"https://codeload.github.com/nebrius/recoil-bootstrap/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nebrius%2Frecoil-bootstrap/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28624341,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-21T02:47:06.670Z","status":"ssl_error","status_checked_at":"2026-01-21T02:45:44.886Z","response_time":86,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["next","nextjs","nextjs13","recoil","recoiljs"],"created_at":"2024-10-28T02:23:49.971Z","updated_at":"2026-01-21T03:01:46.344Z","avatar_url":"https://github.com/nebrius.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Recoil Bootstrap\n\n**Do not use this library** Unfortunately it seems that Recoil has been abandoned by Meta. The lead maintainer was laid off in early 2023, and there have been very few commits and no responses in issue tracker since then. Even more unfortunate for this library: I discovered a bug that stops this library dead in its tracks: https://github.com/facebookexperimental/Recoil/issues/2256. Until Meta restaffs the Recoil project and fixes this bug, this library is not usable in practice. In the mean time, check out a newer library I created called [react-strapped](https://github.com/nebrius/react-strapped) that implements many of the concepts in this library in a state management neutral way.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"version\" src=\"https://img.shields.io/npm/v/recoil-bootstrap\" /\u003e\n  \u003ca href=\"https://bundlephobia.com/package/recoil-bootstrap\"\u003e\n    \u003cimg alt=\"bundle size\" src=\"https://img.shields.io/bundlephobia/min/recoil-bootstrap\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/nebrius/recoil-bootstrap/actions/workflows/tests.yml\"\u003e\n    \u003cimg alt=\"build status\" src=\"https://github.com/nebrius/recoil-bootstrap/actions/workflows/tests.yml/badge.svg\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\nRecoil Bootstrap provides mechanisms that make it straightforward to initialize Recoil with runtime bootstrap data in multi-page applications. Recoil is intentionally small with no runtime dependencies.\n\n- [Motivation](#motivation)\n- [Installation](#installation)\n- [Getting Started](#getting-started)\n- [Multi-page Apps](#multi-page-apps)\n- [Frequently Asked Questions](#frequently-asked-questions)\n  - [Is it possible to reset bootstrap data?](#is-it-possible-to-reset-bootstrap-data)\n  - [Can I reuse root atoms across different bootstrap roots?](#can-i-reuse-root-atoms-across-different-bootstrap-roots)\n  - [Is Recoil Bootstrap server side rendering friendly?](#is-recoil-bootstrap-server-side-rendering-friendly)\n  - [Is Recoil Bootstrap React Server Components friendly?](#is-recoil-bootstrap-react-server-components-friendly)\n- [API Specification](#api-specification)\n  - [`rootAtom(key)`](#rootatomkey)\n  - [`bootstrappedAtom(rootAtom, options)`](#bootstrappedatomrootatom-options)\n  - [`bootstrappedAtomValueHook(bootstrappedAtom)`](#bootstrappedatomvaluehookbootstrappedatom)\n  - [`\u003cBootstrapRoot bootstrapData={} rootAtom={}\u003e...\u003c/BootstrapRoot\u003e`](#bootstraproot-bootstrapdata-rootatombootstraproot)\n- [License](#license)\n\n## Motivation\n\n[Recoil](https://recoiljs.org/) is a popular new state management framework for React with a powerful, yet simple, API. However, initializing Recoil with runtime bootstrap data [is tricky and non-obvious](https://github.com/facebookexperimental/Recoil/issues/750), especially when used with multi-page app frameworks such as [Next.js](https://nextjs.org/). Specific challenges include:\n\n1. In multi-page apps, Recoil must be initialized with bootstrap data that's been prop-drilled to a component\n     - Thus, bootstrap data is not available to use with the `default` property in the atom descriptor, preventing us from using the standard way of initializing atoms.\n     - The recommended way to solve this challenge is to initialize atoms with the `initializeState` property in `RecoilRoot`. As we'll see in challenge 2, this is not viable in multi-page apps.\n2. Bootstrap data varies from page to page\n     - Thus, we need to initialize _different atoms_ depending on which page we're on.\n     - While it's possible, if unwieldy, to use a `switch(currentRoute)` statement to _initialize_ the atoms, we have to statically import _every_ atom from _every_ page to do the initialization.\n     - Dynamic imports are asynchronous, and thus are incompatible with the synchronous `initializeState` prop, which is otherwise the only way to conditionally import atoms based on the current route.\n3. State needs to be scoped to a React context and not used globally\n     - A Next.js server is rendering multiple requests from multiple users more or less at once, meaning global data is not an option since it wouldn't be \"scoped\" to a specific user.\n     - This means we can't do tricks like creating a global promise we attach to each atoms `default` prop, and then resolving it once we get the bootstrap data.\n\nAll of these challenges together mean that Recoil does not currently include any mechanisms for conveniently initializing Recoil with bootstrapped data in multi-page applications. Recoil Bootstrap provides these mechanisms.\n\n## Installation\n\nInstall Recoil Bootstrap from npm with:\n\n```\nnpm install recoil-bootstrap\n```\n\n## Getting Started\n\nRecoil Bootstrap works by creating \"root atoms,\" which are special opaque atoms that hold scoped bootstrap data initialized on first render. These atoms are not accessed directly. To access this data, we then create \"bootstrapped atoms\" which initialize themselves from their root atom. These bootstrapped atoms can then be used to create hooks for reading this data safely, ensuring that code can only access data available in the React component tree the data is intended for.\n\nThis example shows a minimal example using Recoil Bootstrap. It's written in TypeScript to a) demonstrate how TypeScript types flows through the library and b) to give a sense of what data is expected where. You can absolutely use this library without using TypeScript though.\n\nFirst, let's create some atoms in a file called `state.ts`:\n\n```tsx\nimport {\n  rootAtom,\n  bootstrappedAtom,\n  bootstrappedAtomValueHook,\n} from 'recoil-bootstrap';\n\nexport interface MyBootstrapData {\n  currentUser: {\n    name: string;\n    age: number;\n  };\n}\n\n// See how this root atom only takes in a key, and is otherwise unconfigurable.\n// By specifying the shape of data here in the TypeScript generic, we get full\n// typing throughout the rest of our atoms/hooks/components/etc.\nexport const myRootAtom = rootAtom\u003cMyBootstrapData\u003e('myRootAtom');\n\n// Now we create a bootstrapped atom from the root atom to access bootstrap data\nconst currentUserAtom = bootstrappedAtom(myRootAtom, {\n  key: 'currentUserAtom',\n  initialValue: ({ currentUser }) =\u003e currentUser,\n});\n\n// Lastly we create the hook for safely accessing this data. If this hook is\n// called in an incorrect way, such as before initialization or in a\n// non-bootstrapped component, it will error with a human readable message\nexport const useCurrentUser = bootstrappedAtomValueHook(currentUserAtom);\n```\n\nNow let's create some UI in a Next.js page component:\n\n```tsx\nimport { RecoilRoot } from 'recoil';\nimport { BootstrapRoot } from 'recoil-bootstrap';\n\nimport type { MyBootstrapData } from './state';\nimport { myRootAtom, useCurrentUser } from './state';\n\nfunction MyComponent() {\n  // We use the hook created above, which makes sure that we're calling this\n  // hook in a component with \u003cBootstrapRoot\u003e as a parent in the component tree\n  const currentUser = useCurrentUser();\n  return (\n    // Prints \"Hello Philip J Fry\"\n    \u003cdiv\u003eHello {currentUser.name}\u003c/div\u003e\n  );\n}\n\ninterface PageProps {\n  bootstrapData: MyBootstrapData;\n}\n\n// If you're not familiar with Next.js, this function runs on a server and is\n// responsible for fetching bootstrap data. The value of the `props` property is\n// passed as props to the default export in this file.\nexport function getServerSideProps() {\n  const props: PageProps = {\n    bootstrapData: {\n      currentUser: {\n        name: 'Philip J Fry',\n        age: 1_026,\n      },\n    },\n  };\n  return { props };\n}\n\n// This default export is the root component in a Next.js page. The props\n// passed to this component come from the server via `getServerSideProps`\nexport default function MyApp({ bootstrapData }: PageProps) {\n  return (\n    // We create our recoil root as normal, and then create our bootstrap root\n    // with our bootstrap data and bootstrap atom. The \u003cBootstrapRoot\u003e component\n    // does the work of correlating our bootstrapped atoms with bootstrap data.\n    \u003cRecoilRoot\u003e\n      \u003cBootstrapRoot bootstrapData={bootstrapData} rootAtom={myRootAtom}\u003e\n        \u003cMyComponent /\u003e\n      \u003c/BootstrapRoot\u003e\n    \u003c/RecoilRoot\u003e\n  );\n}\n```\n\n## Multi-page Apps\n\nRecoil Bootstrap is designed specifically for multi-page applications, which it enables via multiple `\u003cBootstrapRoot\u003e` components. You can have as many bootstrap roots as you want with any amount of nesting.\n\nIn multi-page applications, we often have a set of bootstrap data that is common to all pages as well as bootstrap data that is specific to a page. With Recoil Bootstrap, you can create one bootstrap root for the common bootstrap data that exists on all pages, and then per-page bootstrap roots that contain those pages' data.\n\nThis would look like:\n\n```tsx\nfunction AppWrapper({ commonBootstrapData, children }) {\n  return (\n    \u003cRecoilRoot\u003e\n      \u003cBootstrapRoot\n        bootstrapData={commonBootstrapData}\n        rootAtom={commonRootAtom}\n      \u003e\n        {children}\n      \u003c/BootstrapRoot\u003e\n    \u003c/RecoilRoot\u003e\n  );\n}\n\nexport default function MyPage({ commonBootstrapData, myPageBootstrapData }) {\n  return (\n    \u003cAppWrapper commonBootstrapData={commonBootstrapData}\u003e\n      \u003cBootstrapRoot\n        bootstrapData={myPageBootstrapData}\n        rootAtom={myPageRootAtom}\n      \u003e\n      \u003c/BootstrapRoot\u003e\n    \u003c/AppWrapper\u003e\n  )\n}\n```\n\nIf bootstrap data exists across a few pages, you can create a third bootstrap root that is shared between these pages.\n\nWhen using multiple roots, hooks for accessing data provide guardrails against accessing data from the wrong place. If you try and call a bootstrapped hook based on `myPageRootAtom` on a different page, then you'll get a human readable error saying you're trying to access it from the wrong place, like so:\n\n\u003cbr /\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"img/access-error.png\" width=\"480\" alt=\"Image showing a hook access error\" /\u003e\n\u003c/p\u003e\n\nFor an in-depth example of a multi-page Next.js app using Recoil Bootstrap, see my [recoil-prototyping](https://github.com/nebrius/recoil-prototyping) repository.\n\n## Frequently Asked Questions\n\n### Is it possible to reset bootstrap data?\n\nNot currently, but it's on the roadmap. See https://github.com/nebrius/recoil-bootstrap/issues/1 for more information.\n\n### Can I reuse root atoms across different bootstrap roots?\n\nNo. When this happens, the last bootstrap root to be initialized will win, and any previous initialization data will be discarded. In addition, once I get support for resetting bootstrap data implemented, this could break client-side routing because one of the roots could be unmounted while the other is still mounted. This would cause the bootstrapped atom to be put back in a loading state and would start throwing exceptions in the still-mounted bootstrap root component tree.\n\n### Is Recoil Bootstrap server side rendering friendly?\n\nYes. Initialization happens synchronously, so all data will be available for the single rendering pass that happens in server side rendering.\n\n### Is Recoil Bootstrap React Server Components friendly?\n\nYes, ish. Recoil Bootstrap works just fine with React Server Components. Each server component that fetches bootstrap data can be assigned its own `\u003cBootstrapRoot\u003e` to contain that component tree's bootstrap data.\n\nThe catch is that hooks cannot be used inside of React Server Components, meaning that Recoil can only be used in client-only components. As such, Recoil Bootstrap is also limited to client-only components.\n\n## API Specification\n\n### `rootAtom(key)`\n\nCreates a root atom.\n\n```ts\nfunction rootAtom\u003cT\u003e(key: string): RecoilState\u003cT\u003e\n```\n\n_**Props:**_\n\n`key`: `string`\n\nThe key to assign to the root atom.\n\n_**Returns:**_\n\nThe root atom to be passed to a corresponding [BootstrapRoot](#BootstrapRoot) component.\n\n### `bootstrappedAtom(rootAtom, options)`\n\nCreates a bootstrapped atom for accessing bootstrap data.\n\n```ts\ntype BootstrappedAtomOptions\u003cAtomValue, BootstrapData\u003e = Omit\u003c\n  AtomOptions\u003cAtomValue\u003e,\n  'default'\n\u003e \u0026 {\n  initialValue: (bootstrapData: BootstrapData) =\u003e AtomValue;\n};\n\nfunction bootstrappedAtom\u003cAtomValue, BootstrapData\u003e(\n  rootAtom: RecoilValue\u003cBootstrapData\u003e,\n  options: BootstrappedAtomOptions\u003cAtomValue, BootstrapData\u003e\n): RecoilState\u003cAtomValue\u003e\n```\n\n_**Props:**_\n\n`rootAtom`: `RecoilValue\u003cBootstrapData\u003e`\n\nThe root atom containing the bootstrap data to initialize this atom with.\n\n`options`: `BootstrappedAtomOptions\u003cAtomValue, BootstrapData\u003e`\n\nOptions here are the mostly the same as the options passed to the built-in `atom()` function in Recoil. The difference is that the `default` property is _not_ allowed, and there is a new `initialValue` function to replace `default`.\n\n`options.initialValue`: `(bootstrapData: BootstrapData) =\u003e AtomValue`\n\nA function to initialize the bootstrapped atom with. This function is called at runtime with all of the bootstrap data passed to [BootstrapRoot](#BootstrapRoot). The atom's value is then set to the value returned from this function.\n\n_**Returns:**_\n\nThe bootstrapped atom that can then be passed to [bootstrappedAtomValueHook](#bootstrappedAtomValueHook) to create a hook for safely accessing this data. The returned atom is a normal off-the-shelf Recoil atom, and can be used accordingly.\n\n_**Throws:**_\n\nThis function will throw an exception if a `default` value is included in `options`.\n\n### `bootstrappedAtomValueHook(bootstrappedAtom)`\n\nCreates a hook for accessing a bootstrapped atom's value safely.\n\n```ts\nfunction bootstrappedAtomValueHook\u003cT\u003e(bootstrappedAtom: RecoilValue\u003cT\u003e): () =\u003e T\n```\n\n_**Props:**_\n\n`bootstrappedAtom`: `RecoilValue\u003cT\u003e`\n\nThe bootstrapped atom to create the accessor hook for\n\n_**Returns:**_\n\nThe hook that accesses the value.\n\n_**Throws:**_\n\nCalling the hook returned from this function in the wrong scope will throw an exception. \"Wrong scope\" is defined as calling this hook in a component that does not have the correct corresponding `BootstrapRoot` further up the component tree as a parent of this component.\n\n### `\u003cBootstrapRoot bootstrapData={} rootAtom={}\u003e...\u003c/BootstrapRoot\u003e`\n\nThis component initializes the supplied root atom and its associated bootstrapped atoms with the supplied bootstrap data.\n\n```ts\ninterface LocalizedStateProps\u003cBootstrapData\u003e {\n  bootstrapData: BootstrapData;\n  rootAtom: RecoilState\u003cBootstrapData\u003e;\n}\n\nfunction BootstrapRoot\u003cBootstrapData\u003e(\n  props: PropsWithChildren\u003cLocalizedStateProps\u003cBootstrapData\u003e\u003e\n): JSX.Element | null\n```\n_**Props:**_\n\n`bootstrapData`: `BootstrapData`\n\nThe bootstrap data to initialize bootstrapped atoms with.\n\n`rootAtom`: `RecoilState\u003cBootstrapData\u003e`\n\nThe root atom to store the bootstrap data, which in turn initializes all bootstrapped atoms associated with it.\n\n## License\n\nMIT License\n\nCopyright (c) 2023 Bryan Hughes\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnebrius%2Frecoil-bootstrap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnebrius%2Frecoil-bootstrap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnebrius%2Frecoil-bootstrap/lists"}