react-fetch-streams
A react hook for using the Streams API with the Fetch API to stream data from a server.
Table of Contents
Installation
You can install this package from NPM:
npm add react-fetch-streams
Or with Yarn:
yarn add react-fetch-streams
CDN
For CDN, you can use unpkg:
https://unpkg.com/react-fetch-streams/dist/index.min.js
The global namespace for react-fetch-streams is reactFetchStreams:
<!-- The UMD build reads a global `React`. React 19 no longer ships a UMD build,
so for <script> usage load React 18's UMD, or use the ESM build on React 19+. -->
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/react-fetch-streams/dist/index.min.js"></script>
<script type="text/javascript">
const {useStream} = reactFetchStreams;
// ...
</script>
On React 19+, drop the UMD build and import the ESM entry through a bundler or an import map instead.
A runnable demo lives in examples/streaming-counter.
Usage
Stream some data from some server:
import React, {useCallback, useState} from 'react';
import {useStream} from 'react-fetch-streams';
const MyComponent = props => {
const [data, setData] = useState({});
const onNext = useCallback(
async res => {
const data = await res.json();
setData(data);
},
[setData]
);
useStream('http://myserver.io/stream', {onNext});
return <React.Fragment>{data.myProp}</React.Fragment>;
};
You can also pass the fetch request init props using fetchParams:
import React, {useCallback, useState} from 'react';
import {useStream} from 'react-fetch-streams';
const fetchParams = {mode: 'cors'};
const MyComponent = props => {
const [data, setData] = useState({});
const onNext = useCallback(
async res => {
const data = await res.json();
setData(data);
},
[setData]
);
useStream('http://myserver.io/stream', {onNext, fetchParams});
return <React.Fragment>{data.myProp}</React.Fragment>;
};
Chunk boundaries:
useStreamcallsonNextwith aResponsewrapping exactly the bytes of eachreader.read()chunk - it does not buffer or split. The network may split one payload across chunks or coalesce several into one, sores.json()per chunk is only safe when the server flushes one complete, self-contained payload per write. For newline-delimited JSON useuseNdjsonStream(below); for SSE useuseEventStream; both frame the stream for you.
Newline-delimited JSON
useNdjsonStream streams NDJSON, buffering across chunk
boundaries (with a streaming decoder, so multi-byte characters survive) and
calling onData with each parsed line:
import React, {useCallback, useState} from 'react';
import {useNdjsonStream} from 'react-fetch-streams';
const MyComponent = props => {
const [rows, setRows] = useState([]);
const onData = useCallback(row => setRows(prev => prev.concat(row)), []);
useNdjsonStream('http://myserver.io/rows', {onData});
return <React.Fragment>{rows.length}</React.Fragment>;
};
For more examples, please check the tests.
Server-Sent Events
For Server-Sent Events,
useEventSource wraps the native EventSource, so the browser handles
reconnection and Last-Event-ID resumption:
import React, {useCallback, useState} from 'react';
import {useEventSource} from 'react-fetch-streams';
const MyComponent = props => {
const [data, setData] = useState({});
const onMessage = useCallback(evt => setData(JSON.parse(evt.data)), []);
// Handle named events with `onEvent: {eventName: handler}`.
useEventSource('http://myserver.io/events', {onMessage});
return <React.Fragment>{data.myProp}</React.Fragment>;
};
EventSource can only issue GET requests and cannot send custom headers or a
body. When you need those (for example an Authorization header), use
useEventStream, which parses SSE over fetch and reconnects with
Last-Event-ID itself:
import React, {useCallback, useState} from 'react';
import {useEventStream} from 'react-fetch-streams';
const fetchParams = {headers: {Authorization: 'Bearer token'}};
const MyComponent = props => {
const [data, setData] = useState({});
const onEvent = useCallback(evt => setData(JSON.parse(evt.data)), []);
useEventStream('http://myserver.io/events', {onEvent, fetchParams});
return <React.Fragment>{data.myProp}</React.Fragment>;
};
onEvent receives {event, data, id}. Reconnection is on by default; pass
retry: false to disable it or retry: <ms> to set the delay.
Browser Support
You can expect this hook to work wherever the following APIs are supported:
Check browserslist.dev for an overview.
Contribute
See AGENTS.md for the full workflow and conventions. In short:
npm ci # install dependencies
npm run lint # oxlint
npm run format # prettier
npm test # vitest
npm run build # tsdown
- Use Conventional Commits.
- Every change ships with tests; keep coverage from dropping.