{"id":13468648,"url":"https://github.com/GoogleChromeLabs/react-adaptive-hooks","last_synced_at":"2025-03-26T05:31:12.509Z","repository":{"id":40005150,"uuid":"220358364","full_name":"GoogleChromeLabs/react-adaptive-hooks","owner":"GoogleChromeLabs","description":"Deliver experiences best suited to a user's device and network constraints","archived":false,"fork":false,"pushed_at":"2024-11-20T14:53:07.000Z","size":1937,"stargazers_count":5125,"open_issues_count":47,"forks_count":115,"subscribers_count":33,"default_branch":"master","last_synced_at":"2025-03-25T18:08:14.197Z","etag":null,"topics":["adaptive-loading","cpu","loading","memory","network","performance","react","react-hooks"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GoogleChromeLabs.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-11-08T01:04:55.000Z","updated_at":"2025-03-20T12:51:22.000Z","dependencies_parsed_at":"2023-02-03T02:01:36.841Z","dependency_job_id":"f0f6950a-4d1f-492a-b935-968c9d82d7be","html_url":"https://github.com/GoogleChromeLabs/react-adaptive-hooks","commit_stats":{"total_commits":64,"total_committers":12,"mean_commits":5.333333333333333,"dds":0.328125,"last_synced_commit":"baad292c62f6c3e77b11d7f0820896050df2c5b4"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GoogleChromeLabs%2Freact-adaptive-hooks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GoogleChromeLabs%2Freact-adaptive-hooks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GoogleChromeLabs%2Freact-adaptive-hooks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GoogleChromeLabs%2Freact-adaptive-hooks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GoogleChromeLabs","download_url":"https://codeload.github.com/GoogleChromeLabs/react-adaptive-hooks/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245597255,"owners_count":20641861,"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":["adaptive-loading","cpu","loading","memory","network","performance","react","react-hooks"],"created_at":"2024-07-31T15:01:15.702Z","updated_at":"2025-03-26T05:31:12.487Z","avatar_url":"https://github.com/GoogleChromeLabs.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","React Hooks","前端开发框架及项目","⚛️ React","🌐 Web Development - Frontend"],"sub_categories":["React Components","React工具库","React-specific libs:"],"readme":"# React Adaptive Loading Hooks \u0026amp; Utilities \u0026middot; ![](https://img.shields.io/github/license/GoogleChromeLabs/react-adaptive-hooks.svg) [![Build Status](https://travis-ci.org/GoogleChromeLabs/react-adaptive-hooks.svg?branch=master)](https://travis-ci.org/GoogleChromeLabs/react-adaptive-hooks) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/react-adaptive-hooks)\n\n\u003e Deliver experiences best suited to a user's device and network constraints (experimental)\n\nThis is a suite of [React Hooks](https://reactjs.org/docs/hooks-overview.html) and utilities for adaptive loading based on a user's:\n\n* [Network via effective connection type](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/effectiveType)\n* [Data Saver preferences](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/saveData)\n* [Device memory](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/deviceMemory)\n* [Logical CPU cores](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorConcurrentHardware/hardwareConcurrency)\n* [Media Capabilities API](https://developer.mozilla.org/en-US/docs/Web/API/Media_Capabilities_API)\n\nIt can be used to add patterns for adaptive resource loading, data-fetching, code-splitting and capability toggling.\n\n## Objective\n\nMake it easier to target low-end devices while progressively adding high-end-only features on top. Using these hooks and utilities can help you give users a great experience best suited to their device and network constraints.\n\n## Installation\n\n`npm i react-adaptive-hooks --save` or `yarn add react-adaptive-hooks`\n\n## Usage\n\nYou can import the hooks you wish to use as follows:\n\n```js\nimport { useNetworkStatus } from 'react-adaptive-hooks/network';\nimport { useSaveData } from 'react-adaptive-hooks/save-data';\nimport { useHardwareConcurrency } from 'react-adaptive-hooks/hardware-concurrency';\nimport { useMemoryStatus } from 'react-adaptive-hooks/memory';\nimport { useMediaCapabilitiesDecodingInfo } from 'react-adaptive-hooks/media-capabilities';\n```\n\nand then use them in your components. Examples for each hook and utility can be found below:\n\n### Network\n\n`useNetworkStatus` React hook for adapting based on network status (effective connection type)\n\n```js\nimport React from 'react';\n\nimport { useNetworkStatus } from 'react-adaptive-hooks/network';\n\nconst MyComponent = () =\u003e {\n  const { effectiveConnectionType } = useNetworkStatus();\n\n  let media;\n  switch(effectiveConnectionType) {\n    case 'slow-2g':\n      media = \u003cimg src='...' alt='low resolution' /\u003e;\n      break;\n    case '2g':\n      media = \u003cimg src='...' alt='medium resolution' /\u003e;\n      break;\n    case '3g':\n      media = \u003cimg src='...' alt='high resolution' /\u003e;\n      break;\n    case '4g':\n      media = \u003cvideo muted controls\u003e...\u003c/video\u003e;\n      break;\n    default:\n      media = \u003cvideo muted controls\u003e...\u003c/video\u003e;\n      break;\n  }\n  \n  return \u003cdiv\u003e{media}\u003c/div\u003e;\n};\n```\n\n`effectiveConnectionType` values can be `slow-2g`, `2g`, `3g`, or `4g`.\n\nThis hook accepts an optional `initialEffectiveConnectionType` string argument, which can be used to provide a `effectiveConnectionType` state value when the user's browser does not support the relevant [NetworkInformation API](https://wicg.github.io/netinfo/). Passing an initial value can also prove useful for server-side rendering, where the developer can pass an [ECT Client Hint](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/client-hints#ect) to detect the effective network connection type.\n\n```js\n// Inside of a functional React component\nconst initialEffectiveConnectionType = '4g';\nconst { effectiveConnectionType } = useNetworkStatus(initialEffectiveConnectionType);\n```\n\n### Save Data\n\n`useSaveData` utility for adapting based on the user's browser Data Saver preferences.\n\n```js\nimport React from 'react';\n\nimport { useSaveData } from 'react-adaptive-hooks/save-data';\n\nconst MyComponent = () =\u003e {\n  const { saveData } = useSaveData();\n  return (\n    \u003cdiv\u003e\n      { saveData ? \u003cimg src='...' /\u003e : \u003cvideo muted controls\u003e...\u003c/video\u003e }\n    \u003c/div\u003e\n  );\n};\n```\n\n`saveData` values can be `true` or `false`.\n\nThis hook accepts an optional `initialSaveData` boolean argument, which can be used to provide a `saveData` state value when the user's browser does not support the relevant [NetworkInformation API](https://wicg.github.io/netinfo/). Passing an initial value can also prove useful for server-side rendering, where the developer can pass a server [Save-Data Client Hint](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/client-hints#save-data) that has been converted to a boolean to detect the user's data saving preference.\n\n```js\n// Inside of a functional React component\nconst initialSaveData = true;\nconst { saveData } = useSaveData(initialSaveData);\n```\n\n### CPU Cores / Hardware Concurrency\n\n`useHardwareConcurrency` utility for adapting to the number of logical CPU processor cores on the user's device.\n\n```js\nimport React from 'react';\n\nimport { useHardwareConcurrency } from 'react-adaptive-hooks/hardware-concurrency';\n\nconst MyComponent = () =\u003e {\n  const { numberOfLogicalProcessors } = useHardwareConcurrency();\n  return (\n    \u003cdiv\u003e\n      { numberOfLogicalProcessors \u003c= 4 ? \u003cimg src='...' /\u003e : \u003cvideo muted controls\u003e...\u003c/video\u003e }\n    \u003c/div\u003e\n  );\n};\n```\n\n`numberOfLogicalProcessors` values can be the number of logical processors available to run threads on the user's device. \n\n### Memory\n\n`useMemoryStatus` utility for adapting based on the user's device memory (RAM)\n\n```js\nimport React from 'react';\n\nimport { useMemoryStatus } from 'react-adaptive-hooks/memory';\n\nconst MyComponent = () =\u003e {\n  const { deviceMemory } = useMemoryStatus();\n  return (\n    \u003cdiv\u003e\n      { deviceMemory \u003c 4 ? \u003cimg src='...' /\u003e : \u003cvideo muted controls\u003e...\u003c/video\u003e }\n    \u003c/div\u003e\n  );\n};\n```\n\n`deviceMemory` values can be the approximate amount of device memory in gigabytes.\n\nThis hook accepts an optional `initialMemoryStatus` object argument, which can be used to provide a `deviceMemory` state value when the user's browser does not support the relevant [DeviceMemory API](https://github.com/w3c/device-memory). Passing an initial value can also prove useful for server-side rendering, where the developer can pass a server [Device-Memory Client Hint](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/client-hints#save-data) to detect the memory capacity of the user's device.\n\n```js\n// Inside of a functional React component\nconst initialMemoryStatus = { deviceMemory: 4 };\nconst { deviceMemory } = useMemoryStatus(initialMemoryStatus);\n```\n\n### Media Capabilities\n\n`useMediaCapabilitiesDecodingInfo` utility for adapting based on the user's device media capabilities.\n\n**Use case:** this hook can be used to check if we can play a certain content type. For example, Safari does not support WebM so we want to fallback to MP4 but if Safari at some point does support WebM it will automatically load WebM videos.\n\n```js\nimport React from 'react';\n\nimport { useMediaCapabilitiesDecodingInfo } from 'react-adaptive-hooks/media-capabilities';\n\nconst webmMediaDecodingConfig = {\n  type: 'file', // 'record', 'transmission', or 'media-source'\n  video: {\n    contentType: 'video/webm;codecs=vp8', // valid content type\n    width: 800, // width of the video\n    height: 600, // height of the video\n    bitrate: 10000, // number of bits used to encode 1s of video\n    framerate: 30 // number of frames making up that 1s.\n  }\n};\n\nconst initialMediaCapabilitiesInfo = { powerEfficient: true };\n\nconst MyComponent = ({ videoSources }) =\u003e {\n  const { mediaCapabilitiesInfo } = useMediaCapabilitiesDecodingInfo(webmMediaDecodingConfig, initialMediaCapabilitiesInfo);\n\n  return (\n    \u003cdiv\u003e\n      \u003cvideo src={mediaCapabilitiesInfo.supported ? videoSources.webm : videoSources.mp4} controls\u003e...\u003c/video\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n`mediaCapabilitiesInfo` value contains the three Boolean properties supported, smooth, and powerEfficient, which describe whether decoding the media described would be supported, smooth, and powerEfficient.\n\nThis utility accepts a [MediaDecodingConfiguration](https://developer.mozilla.org/en-US/docs/Web/API/MediaDecodingConfiguration) object argument and an optional `initialMediaCapabilitiesInfo` object argument, which can be used to provide a `mediaCapabilitiesInfo` state value when the user's browser does not support the relevant [Media Capabilities API](https://developer.mozilla.org/en-US/docs/Web/API/Media_Capabilities_API) or no media configuration was given.\n\n### Adaptive Code-loading \u0026 Code-splitting\n\n#### Code-loading\n\nDeliver a light, interactive core experience to users and progressively add high-end-only features on top, if a user's hardware can handle it. Below is an example using the Network Status hook:\n\n```js\nimport React, { Suspense, lazy } from 'react';\n\nimport { useNetworkStatus } from 'react-adaptive-hooks/network';\n\nconst Full = lazy(() =\u003e import(/* webpackChunkName: \"full\" */ './Full.js'));\nconst Light = lazy(() =\u003e import(/* webpackChunkName: \"light\" */ './Light.js'));\n\nconst MyComponent = () =\u003e {\n  const { effectiveConnectionType } = useNetworkStatus();\n  return (\n    \u003cdiv\u003e\n      \u003cSuspense fallback={\u003cdiv\u003eLoading...\u003c/div\u003e}\u003e\n        { effectiveConnectionType === '4g' ? \u003cFull /\u003e : \u003cLight /\u003e }\n      \u003c/Suspense\u003e\n    \u003c/div\u003e\n  );\n};\n\nexport default MyComponent;\n```\n\nLight.js:\n```js\nimport React from 'react';\n\nconst Light = ({ imageUrl, ...rest }) =\u003e (\n  \u003cimg src={imageUrl} {...rest} /\u003e\n);\n\nexport default Light;\n```\n\nFull.js:\n```js\nimport React from 'react';\nimport Magnifier from 'react-magnifier';\n\nconst Full = ({ imageUrl, ...rest }) =\u003e (\n  \u003cMagnifier src={imageUrl} {...rest} /\u003e\n);\n\nexport default Full;\n```\n\n#### Code-splitting\n\nWe can extend `React.lazy()` by incorporating a check for a device or network signal. Below is an example of network-aware code-splitting. This allows us to conditionally load a light core experience or full-fat experience depending on the user's effective connection speed (via `navigator.connection.effectiveType`).\n\n```js\nimport React, { Suspense } from 'react';\n\nconst Component = React.lazy(() =\u003e {\n  const effectiveType = navigator.connection ? navigator.connection.effectiveType : null\n\n  let module;\n  switch (effectiveType) {\n    case '3g':\n      module = import(/* webpackChunkName: \"light\" */ './Light.js');\n      break;\n    case '4g':\n      module = import(/* webpackChunkName: \"full\" */ './Full.js');\n      break;\n    default:\n      module = import(/* webpackChunkName: \"full\" */ './Full.js');\n      break;\n  }\n\n  return module;\n});\n\nconst App = () =\u003e {\n  return (\n    \u003cdiv className='App'\u003e\n      \u003cSuspense fallback={\u003cdiv\u003eLoading...\u003c/div\u003e}\u003e\n        \u003cComponent /\u003e\n      \u003c/Suspense\u003e\n    \u003c/div\u003e\n  );\n};\n\nexport default App;\n```\n\n## Server-side rendering support\n\nThe built version of this package uses ESM (native JS modules) by default, but is not supported on the server-side. When using this package in a web framework like Next.js with server-rendering, we recommend you\n\n* Transpile the package by installing [next-transpile-modules](https://github.com/martpie/next-transpile-modules). ([example project](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/next-show-adaptive-loading)). This is because Next.js currently does not pass `node_modules` into webpack server-side.\n\n* Use a UMD build as in the following code-snippet: ([example project](https://glitch.com/edit/#!/anton-karlovskiy-next-show-adaptive-loading?path=utils/hooks.js:19:91))\n```\nimport {\n  useNetworkStatus,\n  useSaveData,\n  useHardwareConcurrency,\n  useMemoryStatus,\n  useMediaCapabilitiesDecodingInfo\n} from 'react-adaptive-hooks/dist/index.umd.js';\n```\n\n## Browser Support\n\n* [Network Information API - effectiveType](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/effectiveType) is available in [Chrome 61+, Opera 48+, Edge 76+, Chrome for Android 76+, Firefox for Android 68+](https://caniuse.com/#search=effectiveType)\n\n* [Save Data API](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/saveData) is available in [Chrome 65+, Opera 62+, Chrome for Android 76+, Opera for Android 46+](https://caniuse.com/#search=saveData)\n\n* [Hardware Concurrency API](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorConcurrentHardware/hardwareConcurrency) is available in [Chrome 37+, Safari 10.1+, Firefox 48+, Opera 24+, Edge 15+, Chrome for Android 76+, Safari on iOS 10.3+, Firefox for Android 68+, Opera for Android 46+](https://caniuse.com/#search=navigator.hardwareConcurrency)\n\n* [Performance memory API](https://developer.mozilla.org/en-US/docs/Web/API/Performance) is a non-standard and only available in [Chrome 7+, Opera, Chrome for Android 18+, Opera for Android](https://developer.mozilla.org/en-US/docs/Web/API/Performance/memory)\n\n* [Device Memory API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/deviceMemory) is available in [Chrome 63+, Opera 50+, Chrome for Android 76+, Opera for Android 46+](https://caniuse.com/#search=deviceMemory)\n\n* [Media Capabilities API](https://developer.mozilla.org/en-US/docs/Web/API/Media_Capabilities_API) is available in [Chrome 63+, Firefox 63+, Opera 55+, Chrome for Android 78+, Firefox for Android 68+](https://caniuse.com/#search=media%20capabilities)\n\n## Demos\n\n### Network\n\n* [Network-aware loading](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/cra-network-aware-loading) with create-react-app ([Live](https://adaptive-loading.web.app/cra-network-aware-loading/))\n* [Network-aware code-splitting](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/cra-network-aware-code-splitting) with create-react-app ([Live](https://adaptive-loading.web.app/cra-network-aware-code-splitting/))\n* [Network-aware data-fetching](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/cra-network-aware-data-fetching) with create-react-app ([Live](https://adaptive-loading.web.app/cra-network-aware-data-fetching/))\n\n* [React Movie - network-aware loading](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/react-movie-network-aware-loading) ([Live](https://adaptive-loading.web.app/react-movie-network-aware-loading/))\n* [React Shrine - network-aware code-splitting](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/react-shrine-network-aware-code-splitting) ([Live](https://adaptive-loading.web.app/react-shrine-network-aware-code-splitting/))\n* [React eBay - network-aware code-splitting](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/react-ebay-network-aware-code-splitting) ([Live](https://adaptive-loading.web.app/react-ebay-network-aware-code-splitting/))\n* [React Lottie - network-aware loading](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/react-lottie-network-aware-loading) ([Live](https://adaptive-loading.web.app/react-lottie-network-aware-loading/))\n\n### Save Data\n\n* [React Twitter - save-data loading based on Client Hint](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/react-twitter-save-data-loading(client-hint)) ([Live](https://adaptive-loading.web.app/react-twitter-save-data-loading(client-hint)/))\n* [React Twitter - save-data loading based on Hook](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/react-twitter-save-data-loading(hook)) ([Live](https://adaptive-loading.web.app/react-twitter-save-data-loading(hook)/))\n\n### CPU Cores / Hardware Concurrency\n\n* [Hardware concurrency considerate code-splitting](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/cra-hardware-concurrency-considerate-code-splitting) with create-react-app ([Live](https://adaptive-loading.web.app/cra-hardware-concurrency-considerate-code-splitting/))\n* [Hardware concurrency considerate loading](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/cra-hardware-concurrency-considerate-loading) with create-react-app ([Live](https://adaptive-loading.web.app/cra-hardware-concurrency-considerate-loading/))\n\n### Memory\n\n* [Memory considerate loading](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/cra-memory-considerate-loading) with create-react-app ([Live](https://adaptive-loading.web.app/cra-memory-considerate-loading/))\n* [Memory considerate loading (SketchFab version)](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/cra-memory-considerate-loading-sketchfab) with create-react-app ([Live](https://adaptive-loading.web.app/cra-memory-considerate-loading-sketchfab/))\n* [Memory-considerate animation-toggling](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/cna-memory-considerate-animation) with create-next-app ([Live](https://adaptive-loading.web.app/cna-memory-considerate-animation/))\n\n* [React Dixie Mesh - memory considerate loading](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/react-dixie-memory-considerate-loading) ([Live](https://adaptive-loading.web.app/react-dixie-memory-considerate-loading/))\n\n### Hybrid\n\n* [React Youtube - adaptive loading with mix of network, memory and hardware concurrency](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/react-youtube-adaptive-loading) ([Live](https://adaptive-loading.web.app/react-youtube-adaptive-loading/))\n* [Next Show - adaptive loading with mix of network, memory and Client Hints](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/next-show-adaptive-loading) ([demo](https://adaptive-loading.web.app/next-show-adaptive-loading/))\n\n## References\n\n* [Adaptive serving based on network quality](https://web.dev/adaptive-serving-based-on-network-quality/)\n* [Adaptive Serving using JavaScript and the Network Information API](https://addyosmani.com/blog/adaptive-serving/)\n* [Serving Adaptive Components Using the Network Information API](https://dev.to/vorillaz/serving-adaptive-components-using-the-network-information-api-lbo)\n\n## License\n\nLicensed under the Apache-2.0 license.\n\n## Team\n\nThis project is brought to you by [Addy Osmani](https://github.com/addyosmani) and [Anton Karlovskiy](https://github.com/anton-karlovskiy).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGoogleChromeLabs%2Freact-adaptive-hooks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FGoogleChromeLabs%2Freact-adaptive-hooks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGoogleChromeLabs%2Freact-adaptive-hooks/lists"}