{"id":13827081,"url":"https://github.com/transitive-bullshit/react-suspense-polyfill","last_synced_at":"2025-04-15T22:51:36.644Z","repository":{"id":57345947,"uuid":"146159940","full_name":"transitive-bullshit/react-suspense-polyfill","owner":"transitive-bullshit","description":"Polyfill for the React Suspense API 😮","archived":false,"fork":false,"pushed_at":"2019-02-23T22:21:47.000Z","size":648,"stargazers_count":100,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-29T02:12:20.257Z","etag":null,"topics":["polyfill","react","react-suspense","reactjs","suspense"],"latest_commit_sha":null,"homepage":"https://transitive-bullshit.github.io/react-suspense-polyfill/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/transitive-bullshit.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-08-26T07:18:43.000Z","updated_at":"2024-10-09T07:28:44.000Z","dependencies_parsed_at":"2022-09-17T22:14:05.096Z","dependency_job_id":null,"html_url":"https://github.com/transitive-bullshit/react-suspense-polyfill","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/transitive-bullshit%2Freact-suspense-polyfill","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/transitive-bullshit%2Freact-suspense-polyfill/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/transitive-bullshit%2Freact-suspense-polyfill/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/transitive-bullshit%2Freact-suspense-polyfill/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/transitive-bullshit","download_url":"https://codeload.github.com/transitive-bullshit/react-suspense-polyfill/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249167434,"owners_count":21223505,"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":["polyfill","react","react-suspense","reactjs","suspense"],"created_at":"2024-08-04T09:01:49.650Z","updated_at":"2025-04-15T22:51:36.618Z","avatar_url":"https://github.com/transitive-bullshit.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# react-suspense-polyfill\n\n\u003e Provides a basic polyfill for the upcoming React Suspense APIs.\n\n[![NPM](https://img.shields.io/npm/v/react-suspense-polyfill.svg)](https://www.npmjs.com/package/react-suspense-polyfill) [![Build Status](https://travis-ci.com/transitive-bullshit/react-suspense-polyfill.svg?branch=master)](https://travis-ci.com/transitive-bullshit/react-suspense-polyfill) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)\n\n- [x] [React.Suspense](src/placeholder.js)\n- [x] [React.Timeout](src/timeout.js)\n- [x] Support React `v16`\n- [x] In-depth [blog post](https://hackernoon.com/building-a-polyfill-for-react-suspense-f1c7baf18ca1)\n- [x] Suspense [demo](https://transitive-bullshit.github.io/react-suspense-polyfill/)\n- [ ] Thorough compatibility tests\n\n\n## Status\n\nThis module is intended for understanding and experimenting with the upcoming React Suspense APIs.\n\nNote that the actual version of Suspense that will ship with React is [significantly](https://github.com/facebook/react/pull/12279) [more](https://github.com/facebook/react/pull/13397) [complicated](https://github.com/facebook/react/pull/13398) and efficient than the version in this polyfill. It is meant solely for experimental purposes and to ease the burden of incremental upgrades.\n\n## How It Works\n\nAt its core, React Suspense works by allowing an async component to throw a Promise from its `render` method.\n\nThis polyfill mimics React's internal support for this behavior by implementing an [error boundary](https://github.com/transitive-bullshit/react-suspense-polyfill/blob/master/src/timeout.js#L24) in the [Timeout](src/timeout.js) component. If the error boundary encounters a thrown Promise, it waits until that Promise resolves and then attempts to re-render its children. It also handles falling back to loading content if the Promise takes too long to resolve.\n\nThe reason this polyfill does not support React `v15` is because error boundaries weren't properly supported until React `v16`. If you have ideas on how to add support for React `v15`, submit an [issue](https://github.com/transitive-bullshit/react-suspense-polyfill/issues) and let's discuss!\n\nNote that React will log an error to the console regarding the thrown error, but *this can safely be ignored*. Unfortunately, there is no way to [disable](https://github.com/facebook/react/issues/11098) this error reporting for these types of intentional use cases.\n\nWith that being said, I hope this module and accompanying demos make it easier to get up-to-speed with React Suspense. 😄\n\n\n## Install\n\n```bash\nnpm install --save react-suspense-polyfill\n```\n\n\n## Usage\n\nThe only difference between using this polyfill and a suspense-enabled version of React, is that you must import `{ Suspense }` from `react-suspense-polyfill` instead of from `React`.\n\nWith this minor change, suspense demos and [react-async-elements](https://github.com/palmerhq/react-async-elements) will function as expected.\n\n```js\nimport React, { Component } from 'react'\nimport ReactDOM from 'react-dom'\n\nimport { Suspense } from 'react-suspense-polyfill'\n\nimport { createCache, createResource } from 'simple-cache-provider'\n\nconst sleep = ms =\u003e new Promise(resolve =\u003e setTimeout(resolve, ms))\nconst cache = createCache()\n\n// Loads the Thing component lazily\nconst getThing = createResource(\n  () =\u003e sleep(2000).then(() =\u003e import('./Thing').then(mod =\u003e mod.default)),\n  thing =\u003e thing\n)\n\nconst LazyThing = props =\u003e {\n  const Comp = getThing.read(cache, props)\n  return \u003cComp {...props} /\u003e\n}\n\nclass Example extends Component {\n  render () {\n    return (\n      \u003cReact.Fragment\u003e\n        \u003ch1\u003eSuspense\u003c/h1\u003e\n\n        \u003cSuspense delayMs={500} fallback={\u003cdiv\u003e🌀 'Loading....'\u003c/div\u003e}\u003e\n          \u003cLazyThing /\u003e\n        \u003c/Suspense\u003e\n      \u003c/React.Fragment\u003e\n    )\n  }\n}\n\nReactDOM.render(\u003cExample /\u003e, document.getElementById('root'))\n```\n\nIn this example, the following rendering steps will occur:\n\n1. React will invoke `Example`'s `render` method.\n2. `Suspense` will get rendered which will in turn attempt to render `LazyThing`.\n3. The `LazyThing` will try to load its resource from the cache but fail and throw a `Promise`.\n4. `Suspense` (actually `Timeout` under the hood) will catch this `Promise` in its error boundary `componentDidCatch`.\n5. `Suspense` starts waiting for that Promise to resolve and kicks off a 500ms timeout. Currently, the `Suspense` subtree is rendering nothing.\n6. After 500ms, `Suspense` will timeout and display its `fallback` loading content.\n7. After another 1500ms (2000ms total), the `LazyThing` resource resolves.\n8. `Suspense` realizes it's child has resolved and again attempts to re-render its child.\n9. The `LazyThing` component synchronously renders the previously cached `Thing` component.\n10. All is right with the world 😃\n\n\n## Related\n\n- [blog post](https://hackernoon.com/building-a-polyfill-for-react-suspense-f1c7baf18ca1) - Gives more background and a deeper explanation of how the code works.\n- [react-suspense-starter](https://github.com/palmerhq/react-suspense-starter) - Alternative which bundles a pre-built version of Suspense-enabled React allowing you to experiment with React Suspense right meow.\n- [react-async-elements](https://github.com/palmerhq/react-async-elements) - Suspense-friendly async React elements for common situations.\n- [fresh-async-react](https://github.com/sw-yx/fresh-async-react) - More Suspense stuff (code, demos, and discussions).\n\n\n## License\n\nMIT © [transitive-bullshit](https://github.com/transitive-bullshit)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftransitive-bullshit%2Freact-suspense-polyfill","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftransitive-bullshit%2Freact-suspense-polyfill","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftransitive-bullshit%2Freact-suspense-polyfill/lists"}