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.

The dashboard widgets update together from a single 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:

  1. Open the live dashboard on StackBlitz.
  2. Open the Project panel (left sidebar).
  3. Click the Download control (cloud with a downward arrow) to save a ZIP.
  4. 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:

  1. the speed chart filling with new points every second
  2. the counter incrementing rapidly
  3. the status indicator flipping every five seconds
  4. random employee cells highlighted when they change
  5. 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 OBJECTS is a fully qualified name such as average:speed@OWNER.
  • One delivery means the client needs a single deliveries entry.
  • time on 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.
  • bigState is 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#

  1. utility/fetchOwnerData.js writes temporary credentials.
  2. The publisher creates objects and the delivery, then starts per-object timers.
  3. The React app calls useBigState once for all objects.
  4. Widgets update from delivery-backed bigState, not from publisher return values.

Use your own credentials#

To run against your account instead of demo keys:

  1. Create publisher/.env:
BIGSTATE_OWNER=YOUR_OWNER
BIGSTATE_API_KEY=YOUR_SERVER_API_KEY
  1. Put matching values in client/src/config.json:
{
"owner": "YOUR_OWNER",
"rootApiKey": "YOUR_RESTRICTED_BROWSER_API_KEY"
}
  1. Start publisher and client without the demo bootstrap (for example npm run start:server and npm run start:client from the project root), after pointing the publisher at publisher/.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 install fails on peer dependencies, check that client/.npmrc contains legacy-peer-deps=true.
  • If the image panel stays empty, check that publisher/src/files/ contains frames and that stateSetValueRef completed without errors.
  • A browser 403 usually means the subscriber key needs a broader policy — do not paste a privileged publisher key into the client.

Next steps#

© 2024 BigState