{"id":13608932,"url":"https://github.com/ctrlplusb/react-async-component","last_synced_at":"2025-05-15T01:06:52.901Z","repository":{"id":16094806,"uuid":"79276675","full_name":"ctrlplusb/react-async-component","owner":"ctrlplusb","description":"Resolve components asynchronously, with support for code splitting and advanced server side rendering use cases.","archived":false,"fork":false,"pushed_at":"2022-12-07T09:43:56.000Z","size":1370,"stargazers_count":1444,"open_issues_count":47,"forks_count":62,"subscribers_count":19,"default_branch":"master","last_synced_at":"2025-05-14T08:31:48.363Z","etag":null,"topics":["async-component","code-splitting","react","ssr","webpack"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/ctrlplusb.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}},"created_at":"2017-01-17T22:04:37.000Z","updated_at":"2025-05-06T02:57:32.000Z","dependencies_parsed_at":"2023-01-14T00:30:42.212Z","dependency_job_id":null,"html_url":"https://github.com/ctrlplusb/react-async-component","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ctrlplusb%2Freact-async-component","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ctrlplusb%2Freact-async-component/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ctrlplusb%2Freact-async-component/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ctrlplusb%2Freact-async-component/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ctrlplusb","download_url":"https://codeload.github.com/ctrlplusb/react-async-component/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254130722,"owners_count":22019827,"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":["async-component","code-splitting","react","ssr","webpack"],"created_at":"2024-08-01T19:01:31.104Z","updated_at":"2025-05-15T01:06:47.872Z","avatar_url":"https://github.com/ctrlplusb.png","language":"JavaScript","readme":"# react-async-component 📬\n\nResolve components asynchronously, with support for code splitting and advanced server side rendering use cases.\n\n[![npm](https://img.shields.io/npm/v/react-async-component.svg?style=flat-square)](http://npm.im/react-async-component)\n[![MIT License](https://img.shields.io/npm/l/react-async-component.svg?style=flat-square)](http://opensource.org/licenses/MIT)\n[![Travis](https://img.shields.io/travis/ctrlplusb/react-async-component.svg?style=flat-square)](https://travis-ci.org/ctrlplusb/react-async-component)\n[![Codecov](https://img.shields.io/codecov/c/github/ctrlplusb/react-async-component.svg?style=flat-square)](https://codecov.io/github/ctrlplusb/react-async-component)\n\n```jsx\nconst AsyncProduct = asyncComponent({\n  resolve: () =\u003e System.import('./Product'),\n  LoadingComponent: ({ productId }) =\u003e \u003cdiv\u003eLoading {productId}\u003c/div\u003e, // Optional\n  ErrorComponent: ({ error }) =\u003e \u003cdiv\u003e{error.message}\u003c/div\u003e // Optional\n});\n\n\u003cAsyncProduct productId={1} /\u003e // 🚀\n```\n\n## TOCs\n\n  - [Introduction](#introduction)\n  - [Features](#features)\n  - [Usage](#usage)\n  - [API](#api)\n    - [asyncComponent(config)](#asynccomponentconfig)\n    - [AsyncComponentProvider](#asynccomponentprovider)\n    - [createAsyncContext](#createasynccontext)\n  - [Server Side Rendering](#server-side-rendering)\n  - [FAQs](#faqs)\n\n## Introduction\n\nThis library does not require that you use either Webpack or Babel.  Instead it provides you a generic and \"pure\" Javascript/React API which allows for the expression of lazy-loaded Components.  It's `Promise`-based API naturally allows you to take advantage of modern code splitting APIs (e.g `import()`, `System.import`, `require.ensure`).\n\n## Features\n\n - Supports _any_ major code splitting API.\n - Show a `LoadingComponent` until your component is resolved.\n - Show an `ErrorComponent` if your component resolution fails.\n - Prevents flash-of-content by tracking already resolved Components.\n - Full server side rendering support, allowing client side state rehydration, avoiding React checksum errors.\n\n## Usage\n\nImagine you had the following `Product` component:\n\n```jsx\nexport default function Product({ id }) {\n  return \u003cdiv\u003eProduct {id}\u003c/div\u003e\n}\n```\n\nTo make this asynchronous create a new file that wraps it with `asyncComponent`, like so:\n\n```jsx\nimport { asyncComponent } from 'react-async-component';\n\nexport default asyncComponent({\n  resolve: () =\u003e System.import('./Product')\n});\n```\n\nI recommend that you use the following folder/file structure:\n\n```\n |- components\n    |- AsyncProduct\n       |- index.js   // contains asyncComponent\n       |- Product.js // The component you want resolved asynchronously\n```\n\nNow, you can simply import `AsyncProduct` anywhere in your application and use it exactly as you would any other component.\n\nFor example:\n\n```jsx\nimport AsyncProduct from './components/AsyncProduct'\n\nexport default function MyApp() {\n  return (\n    \u003cdiv\u003e\n      \u003ch1\u003eWelcome to My App\u003c/h1\u003e\n      \u003cAsyncProduct id={1337} /\u003e\n    \u003c/div\u003e\n  )\n}\n```\n\n## API\n\n### `asyncComponent(config)`\n\nThe asynchronous component factory. Config goes in, an asynchronous component comes out.\n\n#### Arguments\n\n  - `config` (_Object_) : The configuration object for the async Component. It has the following properties available:\n    - `resolve` (_() =\u003e Promise\u003cComponent\u003e_) : A function that should return a `Promise` that will resolve the Component you wish to be async.\n    - `LoadingComponent` (_Component_, Optional, default: `null`) : A Component that will be displayed until your async Component is resolved. All props will be passed to it.\n    - `ErrorComponent` (_Component_, Optional, default: `null`) : A Component that will be displayed if any error occurred whilst trying to resolve your component. All props will be passed to it as well as an `error` prop containing the `Error`.\n    - `name` (_String_, Optional, default: `'AsyncComponent'`) : Use this if you would like to name the created async Component, which helps when firing up the React Dev Tools for example.\n    - `autoResolveES2015Default` (_Boolean_, Optional, default: `true`) : Especially useful if you are resolving ES2015 modules. The resolved module will be checked to see if it has a `.default` and if so then the value attached to `.default` will be used. So easy to forget to do that. 😀\n    - `env` (_String_, Optional) : Provide either `'node'` or `'browser'` so you can write your own environment detection. Especially useful when using PhantomJS or ElectronJS to prerender the React App.\n    - `serverMode` (_Boolean_, Optional, default: `'resolve'`) : Only applies for server side rendering applications. Please see the documentation on server side rendering. The following values are allowed.\n      - __`'resolve'`__ - Your asynchronous component will be resolved and rendered on the server.  It's children will\n      be checked to see if there are any nested asynchronous component instances, which will then be processed based on the `serverMode` value that was associated with them.\n      - __`'defer'`__ - Your asynchronous component will _not_ be rendered on the server, instead deferring rendering to the client/browser.\n      - __`'boundary'`__ - Your asynchronous component will be resolved and rendered on the server. However, if it has a nested asynchronous component instance within it's children that component will be ignored and treated as being deferred for rendering in the client/browser instead (it's serverMode will be ignored).\n    We highly recommend that you consider using `defer` as much as you can.\n\n#### Returns\n\nA React Component.\n\n#### Examples\n\n##### `LoadingComponent`\n\n```jsx\nexport default asyncComponent({\n  resolve: () =\u003e import('./Product'),\n  LoadingComponent: ({ id }) =\u003e \u003cdiv\u003eLoading product {id}\u003c/div\u003e\n})\n```\n\n##### `ErrorComponent`\n\n```jsx\nexport default asyncComponent({\n  resolve: () =\u003e import('./Product'),\n  ErrorComponent: ({ error }) =\u003e \u003cdiv\u003e{error.message}\u003c/div\u003e\n})\n```\n\n##### Named chunks\n\n```jsx\nexport default asyncComponent({\n  resolve: () =\u003e new Promise(resolve =\u003e\n    // Webpack's code splitting API w/naming\n    require.ensure(\n      [],\n      (require) =\u003e {\n        resolve(require('./Product'));\n      },\n      'ChunkName'\n    )\n  )\n})\n```\n\n### `\u003cAsyncComponentProvider /\u003e`\n\nCurrently only useful when building server side rendering applications. Wraps your application allowing for efficient and effective use of asynchronous components.\n\n#### Props\n\n - `asyncContext` (_Object_) : Used so that you can gain hooks into the context for server side rendering render tracking and rehydration. See the `createAsyncContext` helper for creating an instance.\n - `rehydrateState` (_Object_, Optional) : Used on the \"browser-side\" of a server side rendering application (see the docs).  This allows you to provide the state returned by the server to be used to rehydrate the client appropriately.\n\n### `createAsyncContext()`\n\nCreates an asynchronous context for use by the `\u003cAsyncComponentProvider /\u003e`.  The context is an object that exposes the following properties to you:\n\n  - `getState()` (_() =\u003e Object_) : A function that when executed gets the current state of the `\u003cAsyncComponentProvider /\u003e`. i.e. which async components resolved / failed to resolve etc.  This is especially useful for server sider rendering applications where you need to provide the server rendered state to the client instance in order to ensure the required asynchronous component instances are resolved prior to render.\n\n## Server Side Rendering\n\n\u003e NOTE: This section only really applies if you would like to have control over the behaviour of how your asyncComponent instances are rendered on the server.  If you don't mind your asyncComponents always being resolved on the client only then you need not do any of the below.  In my opinion there is great value in just server rendering your app shell and having everything else resolve on the client, however, you may have very strict SEO needs - in which case, we have your back.\n\nThis library has been designed for interoperability with [`react-async-bootstrapper`](https://github.com/ctrlplusb/react-async-bootstrapper).\n\n`react-async-bootstrapper` allows us to do a \"pre-render parse\" of our React Element tree and execute an `asyncBootstrap` function that are attached to a components within the tree. In our case the \"bootstrapping\" process involves the resolution of asynchronous components so that they can be rendered \"synchronously\" by the server.  We use this 3rd party library as it allows interoperability with other libraries which also require a \"bootstrapping\" process (e.g. data preloading as supported by [`react-jobs`](https://github.com/ctrlplusb/react-jobs)).\n\nFirstly, install `react-async-bootstrapper`:\n\n```\nnpm install react-async-bootstrapper\n```\n\nNow, let's configure the \"server\" side.  You could use a similar `express` (or other HTTP server) middleware configuration:\n\n```jsx\nimport React from 'react'\nimport { renderToString } from 'react-dom/server'\nimport { AsyncComponentProvider, createAsyncContext } from 'react-async-component' // 👈\nimport asyncBootstrapper from 'react-async-bootstrapper' // 👈\nimport serialize from 'serialize-javascript'\n\nimport MyApp from './shared/components/MyApp'\n\nexport default function expressMiddleware(req, res, next) {\n  //    Create the async context for our provider, this grants\n  // 👇 us the ability to tap into the state to send back to the client.\n  const asyncContext = createAsyncContext()\n\n  // 👇 Ensure you wrap your application with the provider.\n  const app = (\n    \u003cAsyncComponentProvider asyncContext={asyncContext}\u003e\n      \u003cMyApp /\u003e\n    \u003c/AsyncComponentProvider\u003e\n  )\n\n  // 👇 This makes sure we \"bootstrap\" resolve any async components prior to rendering\n  asyncBootstrapper(app).then(() =\u003e {\n      // We can now render our app 👇\n      const appString = renderToString(app)\n\n      // Get the async component state. 👇\n      const asyncState = asyncContext.getState()\n\n      const html = `\n        \u003chtml\u003e\n          \u003chead\u003e\n            \u003ctitle\u003eExample\u003c/title\u003e\n          \u003c/head\u003e\n          \u003cbody\u003e\n            \u003cdiv id=\"app\"\u003e${appString}\u003c/div\u003e\n            \u003cscript type=\"text/javascript\"\u003e\n              // Serialise the state into the HTML response 👇\n              window.ASYNC_COMPONENTS_STATE = ${serialize(asyncState)}\n            \u003c/script\u003e\n          \u003c/body\u003e\n        \u003c/html\u003e`\n\n      res.send(html)\n    })\n}\n```\n\nThen on the \"client\" side you would do the following:\n\n```jsx\nimport React from 'react'\nimport { render } from 'react-dom'\nimport { AsyncComponentProvider, createAsyncContext } from 'react-async-component' // 👈\nimport asyncBootstrapper from 'react-async-bootstrapper' // 👈\nimport MyApp from './components/MyApp'\n\n// 👇 Get any \"rehydrate\" state sent back by the server\nconst rehydrateState = window.ASYNC_COMPONENTS_STATE\n\n//   Ensure you wrap your application with the provider,\n// 👇 and pass in the rehydrateState.\nconst app = (\n  \u003cAsyncComponentProvider  rehydrateState={rehydrateState}\u003e\n    \u003cMyApp /\u003e\n  \u003c/AsyncComponentProvider\u003e\n)\n\n//   We run the bootstrapper again, which in this context will\n//   ensure that all components specified by the rehydrateState\n// 👇 will be resolved prior to render.\nasyncBootstrapper(app).then(() =\u003e {\n  // 👇 Render the app\n  render(app, document.getElementById('app'))\n});\n```\n\n### SSR AsyncComponent Resolution Process\n\nIt is worth us highlighting exactly how we go about resolving and rendering your `asyncComponent` instances on the server. This knowledge will help you become aware of potential issues with your component implementations as well as how to effectively use our provided configuration properties to create more efficient implementations.\n\nWhen running `react-async-bootstrapper` on the server the helper has to walk through your react element tree (depth first i.e. top down) in order to discover all the `asyncComponent` instances and resolve them in preparation for when you call the `ReactDOM.renderToString`. As it walks through the tree it has to call the `componentWillMount` method on your Components and then the `render` methods so that it can get back the child react elements for each Component and continue walking down the element tree. When it discovers an `asyncComponent` instance it will first resolve the Component that it refers to and then it will continue walking down it's child elements (unless you set the configuration for your `asyncComponent` to not allow this) in order to try and discover any nested `asyncComponent` instances.  It continues doing this until it exhausts your element tree.\n\nAlthough this operation isn't as expensive as an actual render as we don't generate the DOM it can still be quite wasteful if you have a deep tree.  Therefore we have provided a set of configuration values that allow you to massively optimise this process. See the next section below.\n\n### SSR Performance Optimisation\n\nAs discussed in the [\"SSR AsyncComponent Resolution Process\"](#ssr-asynccomponent-resolution-process) section above it is possible to have an inefficient implementation of your `asyncComponent` instances.  Therefore we introduced a new configuration object property for the `asyncComponent` factory, called `serverMode`, which provides you with a mechanism to optimise the configuration of your async Component instances.  Please see the API documentation for more information.\n\nUnderstand your own applications needs and use the options appropriately . I personally recommend using mostly \"defer\" and a bit of \"boundary\". Try to see code splitting as allowing you to server side render an application shell to give the user perceived performance. Of course there will be requirements otherwise (SEO), but try to isolate these components and use a \"boundary\" as soon as you feel you can.\n\n\n## Demo\n\nYou can see a \"live\" version [here](https://react-universally.now.sh/). This is a deployment of the [\"React, Universally\"](https://github.com/ctrlplusb/react-universally) starter kit that makes use of this library. Open the network tab and then click the menu items to see the asynchronous component resolving in action.\n\n## FAQs\n\n\u003e Let me know if you have any...\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fctrlplusb%2Freact-async-component","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fctrlplusb%2Freact-async-component","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fctrlplusb%2Freact-async-component/lists"}