Deliver experiences best suited to a user's device and network constraints (experimental)
This is a suite of React Hooks and utilities for adaptive loading based on a user's:
It can be used to add patterns for adaptive resource loading, data-fetching, code-splitting and capability toggling.
Make 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.
npm i react-adaptive-hooks --save
You can import the hooks you wish to use as follows:
import { useNetworkStatus } from 'react-adaptive-hooks/network';
import { useSaveData } from 'react-adaptive-hooks/save-data';
import { useHardwareConcurrency } from 'react-adaptive-hooks/hardware-concurrency';
import { useMemoryStatus } from 'react-adaptive-hooks/memory';
and then use them in your components. Examples for each hook and utility can be found below:
useNetworkStatus
React hook for getting network status (effective connection type)
import React from 'react';
import { useNetworkStatus } from 'react-adaptive-hooks/network';
const MyComponent = () => {
const { effectiveConnectionType } = useNetworkStatus();
let media;
switch(effectiveConnectionType) {
case 'slow-2g':
media = <img src='...' alt='low resolution' />;
break;
case '2g':
media = <img src='...' alt='medium resolution' />;
break;
case '3g':
media = <img src='...' alt='high resolution' />;
break;
case '4g':
media = <video muted controls>...</video>;
break;
default:
media = <video muted controls>...</video>;
break;
}
return <div>{media}</div>;
};
useSaveData
Utility for getting Save Data whether it's Lite mode enabled or not
import React from 'react';
import { useSaveData } from 'react-adaptive-hooks/save-data';
const MyComponent = () => {
const { saveData } = useSaveData();
return (
<div>
{ saveData ? <img src='...' /> : <video muted controls>...</video> }
</div>
);
};
useHardwareConcurrency
Utility for getting the number of logical CPU processor cores of the user's device
import React from 'react';
import { useHardwareConcurrency } from 'react-adaptive-hooks/hardware-concurrency';
const MyComponent = () => {
const { numberOfLogicalProcessors } = useHardwareConcurrency();
return (
<div>
{ numberOfLogicalProcessors <= 4 ? <img src='...' /> : <video muted controls>...</video> }
</div>
);
};
useMemoryStatus
Utility for getting memory status of the device
import React from 'react';
import { useMemoryStatus } from 'react-adaptive-hooks/memory';
const MyComponent = () => {
const { deviceMemory } = useMemoryStatus();
return (
<div>
{ deviceMemory < 4 ? <img src='...' /> : <video muted controls>...</video> }
</div>
);
};
Deliver a light, interactive core experience to users and progressively add high-end-only features on top, if a users hardware can handle it. Below is an example using the Network Status hook:
import React, { Suspense, lazy } from 'react';
import { useNetworkStatus } from 'react-adaptive-hooks/network';
const Full = lazy(() => import(/* webpackChunkName: "full" */ './Full.js'));
const Light = lazy(() => import(/* webpackChunkName: "light" */ './Light.js'));
function MyComponent() {
const { effectiveConnectionType } = useNetworkStatus();
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
{ effectiveConnectionType === '4g' ? <Full /> : <Light /> }
</Suspense>
</div>
);
}
export default MyComponent;
Light.js:
import React from 'react';
const Light = ({ imageUrl, ...rest }) => (
<img src={imageUrl} alt='product' {...rest} />
);
export default Light;
Full.js:
import React from 'react';
import Magnifier from 'react-magnifier';
const Heavy = ({ imageUrl, ...rest }) => (
<Magnifier src={imageUrl} {...rest} />
);
export default Full;
We 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
).
import React, { Suspense } from 'react';
const Component = React.lazy(() => {
const effectiveType = navigator.connection ? navigator.connection.effectiveType : null
switch (effectiveType) {
case "3g":
return import(/* webpackChunkName: "light" */ "./light.js");
break;
case "4g":
return import(/* webpackChunkName: "full" */ "./full.js");
break;
default:
return import(/* webpackChunkName: "full" */ "./full.js")
}
});
function App() {
return (
<div className="App">
<header className="App-header">
<Suspense fallback={<div>Loading...</div>}>
<Component />
</Suspense>
</header>
</div>
);
}
export default App;
-
Network Information API - effectiveType is available in Chrome 61+, Opera 48+, Edge 76+, Chrome for Android 76+, Firefox for Android 68+
-
Save Data API is available in Chrome 65+, Opera 62+, Chrome for Android 76+, Opera for Android 46+
-
Hardware Concurrency API 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+
-
Performance memory API is a non-standard and only available in Chrome 7+, Opera, Chrome for Android 18+, Opera for Android
-
Device Memory API is available in Chrome 63+, Opera 50+, Chrome for Android 76+, Opera for Android 46+
-
Network-aware loading with create-react-app (Live)
-
Network-aware code-splitting with create-react-app (Live)
-
Network-aware data-fetching with create-react-app (Live)
- React Twitter - save-data loading based on Client Hint (Live)
- React Twitter - save-data loading based on Hook (Live)
- Hardware concurrency considerate code-splitting with create-react-app (Live)
- Hardware concurrency considerate loading with create-react-app (Live)
-
Memory considerate loading with create-react-app (Live)
-
Memory considerate loading (SketchFab version) with create-react-app (Live)
-
Memory-considerate animation-toggling with create-next-app (Live)
- Adaptive serving based on network quality
- Adaptive Serving using JavaScript and the Network Information API
- Serving Adaptive Components Using the Network Information API
Licensed under the Apache-2.0 license.
This project is brought to you by Addy Osmani and Anton Karlovskiy.