Run a live dashboard with React
This tutorial uses a ready-made starter: a Node.js publisher writes five BigState object streams, and a React dashboard receives them through one WebSocket delivery.
What you will run
A publisher and a React dashboard with five live widgets on one delivery.
What you will learn
How one useBigState subscription can drive many UI panels, including valueRef files.
What you need
Node.js 22.12+. Demo credentials are created automatically when you start the app.
Node publisher → five object states → WebSocket delivery → React dashboard
Speed chart
`average:speed@OWNER` — JSON value `{ speed }`, updated every second.
Counter
`easy:counter@OWNER` — increments `{ count }` ten times per second.
Status
`active:status@OWNER` — toggles `{ status: true | false }` every five seconds.
Employees table
`employees:data@OWNER` — patches random fields in a JSON array every 500 ms.
The fifth object, image:sequence:player@OWNER, publishes valueRef pointers to PNG frames and demonstrates how to load binary state in the browser with getFile.
Keep privileged keys off the client
The starter uses ephemeral demo credentials for local runs.
On start, the project calls a demo-owner endpoint and writes temporary keys into config files. In your own app, publish from a trusted backend and give the browser a restricted key. Never ship a root production key in the client bundle.
Before you begin#
You need Node.js 22.12 or later.
You can also open the project in the browser without downloading: live StackBlitz demo. The same starter lives in bigstate-examples.
Download the starter#
Clone from GitHub:
git clone https://github.com/bigstateio/bigstate-examples.git
cd bigstate-examples/javascript/react/live-dashboard
Or download from StackBlitz:
- Open the live dashboard on StackBlitz.
- Open the Project panel (left sidebar).
- Click the Download control (cloud with a downward arrow) to save a ZIP.
- Unzip the archive on your machine and open that folder in a terminal.
You should see this layout:
├── client/ # React dashboard (Black Dashboard UI)
├── publisher/ # Node.js publisher
├── utility/ # Fetches temporary demo credentials on start
└── package.json
Run the app#
From the project root:
npm install
npm run start:all
start:all fetches temporary demo credentials, then starts the publisher and the React client together.
Open the React URL printed in the terminal (usually http://localhost:3000).
You should see:
- the speed chart filling with new points every second
- the counter incrementing rapidly
- the status indicator flipping every five seconds
- random employee cells highlighted when they change
- PNG frames cycling in the image panel
How the code works#
The publisher writes five object streams. The React client opens one WebSocket delivery and drives every widget from bigState.
Project roles#
publisher/
Creates objects and the delivery, then publishes state on timers.
client/src/views/Dashboard.js
Subscribes with useBigState and renders the widgets.
utility/fetchOwnerData.js
Requests temporary owner and rootApiKey for the demo.
Objects and delivery#
In publisher/src/constants.js, each entry is an object name plus publish settings. One delivery selects all of them:
export const DELIVERY = {
name: 'Dashboard WS delivery',
objects: Object.keys(OBJECTS),
type: DeliveryType.WS,
};
- Each key in
OBJECTSis a fully qualified name such asaverage:speed@OWNER. - One delivery means the client needs a single
deliveriesentry. timeon each config controls how often that object is published.
Publishing JSON and valueRef#
Most widgets publish JSON:
await client.stateSet(objectName, {
at: new Date().toISOString(),
value,
});
The image player uploads frames first, then publishes a valueRef:
await client.stateSetValueRef(
{ object: objectName, filename: 'frame.png', filemimetype: 'image/png' },
fileBuffer,
);
await client.stateSet(objectName, {
at: new Date().toISOString(),
valueRef,
});
withLastState: true reads the previous state before building the next employee list or image frame.
One hook, many objects#
client/src/views/Dashboard.js calls useBigState once:
const { bigState, getFile } = useBigState(
OBJECTS,
{
baseUrl: 'https://api.bigstate.dev',
apiKey: configFile?.rootApiKey ?? '',
},
{
baseUrl: 'https://ws.delivery.bigstate.dev',
apiKey: configFile?.rootApiKey ?? '',
deliveries: [DELIVERY_DASHBOARD],
},
);
- Pass every object name in the first argument.
bigStateis keyed by object name.- One delivery name covers all five streams.
- Credentials come from
client/src/config.json(written by the demo bootstrap).
Widgets from bigState#
useEffect(() => {
const speed = bigState?.[OBJECTS[0]]?.currState;
if (!speed) return;
setSpeedData(previous =>
[...previous, { at: speed.at, value: speed.value?.speed ?? null }].slice(-45),
);
}, [bigState?.[OBJECTS[0]]?.currState]);
Each widget watches its own slice of bigState and updates local UI state. Counter and status can read currState.value directly in JSX.
Binary frames with getFile#
const getUrl = async valueRef => {
const blob = await getFile({
object: OBJECTS[4],
valueRef,
});
if (!blob) return;
const url = URL.createObjectURL(blob);
setImageUrl(url);
};
When currState.valueRef changes, download the file over HTTP and render it with an object URL. Revoke the previous URL to avoid leaks.
Startup order#
utility/fetchOwnerData.jswrites temporary credentials.- The publisher creates objects and the delivery, then starts per-object timers.
- The React app calls
useBigStateonce for all objects. - Widgets update from delivery-backed
bigState, not from publisher return values.
Use your own credentials#
To run against your account instead of demo keys:
- Create
publisher/.env:
BIGSTATE_OWNER=YOUR_OWNER
BIGSTATE_API_KEY=YOUR_SERVER_API_KEY
- Put matching values in
client/src/config.json:
{
"owner": "YOUR_OWNER",
"rootApiKey": "YOUR_RESTRICTED_BROWSER_API_KEY"
}
- Start publisher and client without the demo bootstrap (for example
npm run start:serverandnpm run start:clientfrom the project root), after pointing the publisher atpublisher/.env.
If you do not have credentials yet, follow Create an account and save your credentials.
Troubleshooting#
- Confirm you unzipped the full project and ran commands from the project root.
- If
npm installfails on peer dependencies, check thatclient/.npmrccontainslegacy-peer-deps=true. - If the image panel stays empty, check that
publisher/src/files/contains frames and thatstateSetValueRefcompleted without errors. - A browser 403 usually means the subscriber key needs a broader policy — do not paste a privileged publisher key into the client.