> For the complete documentation index, see [llms.txt](https://beamline.gitbook.io/beamline-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://beamline.gitbook.io/beamline-docs/guides/build-an-analytics-dashboard-in-react.md).

# Build an analytics dashboard in React

A SaaS analytics dashboard in React on your own metrics API: KPIs, revenue, channels, plan mix and uptime under one set of filters, with honest missing data, loading placeholders and a failed state.

> The original of this page is on beamline.io: [beamline.io/guides/react-analytics-dashboard](https://beamline.io/guides/react-analytics-dashboard).

By Danylo Pravda, 2026-10-11

A dashboard is where people decide whether they trust your product's numbers. The usual generated dashboard breaks that trust in small ways: a metric that failed to load shows 0, a chart jumps when its data lands, a percentage change is green when the number that rose is churn. Beamline's [analytics dashboard](https://beamline.io/components/analytics-dashboard) is a SaaS home screen built to avoid each of those. This guide covers what it shows, what you give it, and the parts it is made of, so you can compose a different dashboard from them.

![The Beamline analytics dashboard: KPIs, revenue, channels, plan mix and uptime](https://beamline.io/components/og/analytics-dashboard.png)

## One hook around your metrics API

The dashboard asks one hook for its data, with the range and segment the person picked:

Integration sketch: supply your useCachedRequest hook, returning typed AnalyticsData, an Error or null, and a reload callback.

```tsx
import { AnalyticsDashboard, type UseAnalytics } from "@beamline/analytics-dashboard";

// your data layer, cached by query (React Query, SWR or your own)
const useMetrics: UseAnalytics = ({ range, segment }) => {
  const { data, error, reload } = useCachedRequest(`/api/metrics?segment=${segment}`, range);
  return { data: data ?? null, error, retry: reload }; // null while loading
};

<AnalyticsDashboard
  useData={useMetrics}
  segments={[
    { value: "all", label: "All" },
    { value: "self-serve", label: "Self-serve" },
    { value: "enterprise", label: "Enterprise" },
  ]}
/>
```

The hook is called on every render with the current query, so cache by query (React Query, SWR or your own). `data` is `null` while loading, and `error` with `retry` drive the failed state, which keeps the filters and offers Retry.

## What the data looks like

* **KPIs:** a label, a value, a format and the change against the previous period. `invert` marks a number where a fall is good (churn, latency), so its colour and arrow say good when it drops.
* **Revenue:** new and expansion recurring revenue per day, week or month.
* **Channels:** sign-ups by acquisition channel.
* **Plan mix:** the share of active accounts by plan, whole percentages adding to 100.
* **Uptime:** a service's daily status for the last 90 days, with a summary you measured.
* **Activity:** recent events for the feed.

## Missing is not zero

Every value can be `null`, and `null` means "not measured", not 0. A KPI that was not measured for the range is drawn hatched, a channel that was not tracked shows a hatched bar and "Not measured" in the ranking, and a gap in revenue stays a gap in the chart. A dashboard that turns a failed query into 0 tells people their revenue collapsed; this one tells them the number is missing. The same rule runs through every Beamline chart and the [metric card](https://beamline.io/components/metric-card).

## The parts, if your business is not a SaaS

The block's data is a SaaS's (recurring revenue, plans, uptime). For invoices, orders or bookings, compose the same parts with your own data:

| part                                                        | for                                                                                                                         |
| ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| [Metric card](https://beamline.io/components/metric-card)   | a headline number: the value rolls to each new number, the change has an arrow, a sign and a tone, a trend line and context |
| [Area chart](https://beamline.io/components/area-chart)     | amounts over time, overlapping, stacked or as shares of 100 %                                                               |
| [Bar list](https://beamline.io/components/bar-list)         | a ranking: top pages, sources, customers                                                                                    |
| [Category bar](https://beamline.io/components/category-bar) | one bar split into labelled ranges, such as a plan mix                                                                      |
| [Tracker](https://beamline.io/components/tracker)           | a row of status blocks for uptime, job runs or daily checks                                                                 |
| [App shell](https://beamline.io/components/app-shell)       | the frame: a collapsible sidebar, the top bar and the page                                                                  |

For exploring rows one by one, the dashboard is the wrong screen: that is the [data grid](https://beamline.io/components/data-grid) or the [CRM](https://beamline.io/components/crm) block. For live market screens, it is the [trading terminal](https://beamline.io/components/trading-terminal).

## Small things it gets right

* **Loading keeps the layout still.** Metric cards, bar lists, trackers and category bars draw placeholders in their own loaded shape, at their real height, so nothing moves when the data lands.
* **Labels never collide.** On the plan mix, a boundary label that would touch its neighbour hides, and the ends always show.
* **Switching a filter is measured.** One click on a segment drives the whole page, so Beamline's speed bench measures that click; a fix in the chart frame halved the React commits it causes, 132 to 70 ([the measurements](https://beamline.io/guides/react-performance-lessons)).
* **The keyboard reaches everything.** Every filter is a native control, ⌘B or Ctrl+B collapses the sidebar, and chart legend items toggle their series with Enter.

## Ask your agent for it

With Beamline connected ([setup for your agent](https://beamline.io/connect)): "an analytics home for our SaaS on /api/metrics with range and segment filters, MRR, sign-ups by channel and uptime". The agent installs the block, writes the hook around your endpoint and maps your fields onto its data, keeping missing values as `null`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://beamline.gitbook.io/beamline-docs/guides/build-an-analytics-dashboard-in-react.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
