Key answer: A React dashboard UI is a layout shell (sidebar, header, content region) filled with a small set of repeating parts — stat cards, data tables, and charts — each of which must render four states: loading, empty, error, and data. An AI coding assistant can scaffold all of this quickly, but it defaults to the happy path; the value you add is demanding the missing states, the accessibility, and owned components so the result doesn't look generic.
Table of contents
- The dashboard shell
- The parts: cards, tables, charts
- The four states people forget
- Accessibility in a data-dense UI
- Not looking generic
- An AI-assisted workflow
- Common mistakes
- FAQ
- Conclusion
The dashboard shell
Almost every dashboard shares the same frame: a persistent sidebar for navigation, a header with context and account controls, and a scrollable content region that swaps per route. Building this well once — as a real layout with landmark regions — pays off on every screen after.
// components/dashboard-shell.tsx
export function DashboardShell({
nav, header, children,
}: { nav: React.ReactNode; header: React.ReactNode; children: React.ReactNode }) {
return (
<div className="shell">
<aside aria-label="Primary"><nav>{nav}</nav></aside>
<div className="shell-main">
<header>{header}</header>
<main id="content" tabIndex={-1}>{children}</main>
</div>
</div>
);
}
An AI coding assistant scaffolds this shape well because it is conventional. Give it the semantic landmarks (aside, nav, header, main) explicitly — left to itself it often nests divs and loses the structure screen readers rely on. Owned layout components like these are the foundation of a coherent set of dashboard components, which is why AETumi ships the shell and its landmarks as reviewed source rather than leaving the assistant to reinvent them.
The parts: cards, tables, charts
The content region is built from a handful of reusable parts:
- Stat cards — a label, a value, an optional delta and sparkline. Presentational; they can be Server Components if you use the App Router.
- Data tables — the workhorse. Sortable headers, optional pagination, row selection. This is where most complexity and most accessibility bugs live.
- Charts — line, bar, area. Interactive ones (range toggles, tooltips) are client components. Do not let a chart be the only representation of a number a user might need to read exactly.
// components/stat-card.tsx
export function StatCard({ label, value, delta }: {
label: string; value: string; delta?: number;
}) {
return (
<article className="stat-card">
<p className="stat-label">{label}</p>
<p className="stat-value">{value}</p>
{delta != null && (
<p className={delta >= 0 ? "up" : "down"}>
{delta >= 0 ? "+" : ""}{delta}%
</p>
)}
</article>
);
}
For a data table, use a real <table> with <th scope="col"> headers. AI assistants frequently render tables as grids of divs, which strips the semantics that make the data navigable. Insist on the native element unless you have a specific reason not to.
The four states people forget
This is the single most valuable thing to get right, and the thing AI-generated dashboards most reliably miss. Every data-driven part has four states, not one:
- Loading — a skeleton or spinner with
aria-busyso the layout doesn't jump and assistive tech announces the wait. - Empty — a genuine "no data yet" message with a next action, not a blank rectangle. New accounts and filtered-to-nothing views hit this constantly.
- Error — a clear message and a retry affordance. The fetch will fail eventually; decide what the user sees when it does.
- Data — the happy path the assistant gives you by default.
// A small state machine every data part should honor
function AsyncPanel<T>({ state, children, onRetry }: {
state: { status: "loading" } | { status: "empty" }
| { status: "error"; message: string } | { status: "ready"; data: T };
children: (data: T) => React.ReactNode;
onRetry?: () => void;
}) {
switch (state.status) {
case "loading": return <Skeleton aria-busy="true" />;
case "empty": return <EmptyState title="Nothing here yet" />;
case "error": return <ErrorState message={state.message} onRetry={onRetry} />;
case "ready": return <>{children(state.data)}</>;
}
}
Modeling the states as a union type like this makes the missing branches impossible to ignore — TypeScript forces you to handle each one. When you prompt your assistant, ask for all four explicitly; "build a table for this data" will get you only the last. AETumi's dashboard parts model these four states out of the box, so the loading, empty, and error branches are present before the assistant touches them.
Accessibility in a data-dense UI
Dashboards pack a lot of interaction into a small space, which makes accessibility easy to lose and important to keep:
- Every control reachable and operable by keyboard, with a visible focus ring.
- Real semantics:
table/thfor tables,buttonfor actions,navfor navigation, headings in order. - A skip link to
#contentso keyboard users bypass the sidebar. - Live updates (a value that refreshes) announced with a polite
aria-liveregion rather than changing silently. - Charts given a text alternative — a caption, a data table, or an accessible summary — since color and shape alone exclude some users.
Treat the assistant's accessibility output as a draft. A quick keyboard-only pass through the dashboard surfaces most of what it missed.
Not looking generic
AI-assembled dashboards tend to converge on the same look because they are regenerated from the same generic priors. The fix is ownership: build from your own components and tokens so every screen inherits your spacing, type, color, and radius instead of a default. Starting from a themable source like AETumi means that ownership is there from the first screen. When the primitives are yours and themable, an assistant filling them produces something that looks like your product, not like every other AI dashboard. For the bigger picture of AI assembling UIs from owned parts, see how AI coding assistants build React UIs.
An AI-assisted workflow
1. Build or import the shell and the parts (StatCard, DataTable, Chart wrapper) with your tokens and semantics — or start from AETumi's dashboard components so the shell, parts, and states already exist. 2. Prompt per screen in terms of those parts, and explicitly ask for loading, empty, and error states for anything that fetches. 3. Wire real data — fetch on the server where you can, keep interactive charts as client islands. 4. Do a keyboard and screen-reader pass; fix what the assistant left as div-with-onClick. 5. Verify the empty and error paths by actually forcing them, not just imagining them.
If your dashboard includes 3D or WebGL visuals, the react-three-fiber-examples repo shows accessible, performant patterns for embedding React Three Fiber scenes inside a React app.
Common mistakes
- Shipping only the happy path — no loading, empty, or error UI.
- Rendering tables as
divgrids and losing all table semantics. - Making a chart the only way to read a number; provide a text or table alternative.
- Hardcoding colors so the dashboard can't theme or do dark mode.
- Putting the whole page in a client component when only the chart needs state.
- Accepting the assistant's accessibility without a keyboard test.
FAQ
What are the "states people forget" in a dashboard? Loading, empty, and error. AI-generated dashboards almost always render only the data (happy-path) state. Ask for all four explicitly and model them as a union type so none can be skipped.
Should dashboard charts be Server or Client Components? Interactive charts (range toggles, tooltips, hover) must be Client Components. Static stat cards and the layout can stay Server Components in the App Router, which keeps your client bundle smaller.
How do I make an AI-built dashboard not look generic? Build it from components and design tokens you own, and have the assistant fill those rather than generate UI from scratch. Owned, themable primitives carry your brand into every screen. A source like AETumi gives you those primitives up front.
How should a data table stay accessible? Use a real <table> with <th scope="col">, keyboard-operable sort controls, a visible focus ring, and a caption or summary. Avoid recreating tables from divs.
Conclusion
A React dashboard UI is a shell plus a few repeating parts, and building it with an AI coding assistant is fast — as long as you supply what the assistant leaves out: the loading, empty, and error states, real accessibility, and owned components so it looks like your product. Model the states as types, keep the semantics native, theme from tokens, and review by keyboard. AETumi, an AI-native 3D web platform, gives you production-ready React and Next.js dashboard components, 3D scenes, and MCP workflows for AI assistants — a source you own and adapt, buy once and own for life. Start from our dashboard components, see the plans at aetumi.app/pricing, and let your assistant assemble, not improvise.
More from the AETumi library
Real, production-ready assets — preview the motion, grab the source.

