Two live React demos
Most portfolios tell you someone can use React. This post shows it. Below are two working demos running live on this page, a streaming AI chat interface and an interactive data dashboard example.
I built both the way I approach production work:
- TypeScript with a clean separation between logic and view,
- an eye on Core Web Vitals,
- accessibility baked in from the first line, and a design that holds up on a phone and in dark mode.
This article walks through the interesting decisions behind each one.
Demo 1: Streaming AI chat
Ask it about Next.js, streaming, performance, or accessibility. Watch the reply arrive token-by-token, and hit Stop mid-stream to see cancellation work end-to-end.
The idea
The streaming here is simulated client-side so the demo runs with no backend or API key. But the mechanics are the real thing because it's the same pattern I'd use against an LLM endpoint. The key insight is that the UI never cares where the tokens come from, it just consumes an async iterator. Here's the streaming source, typed:
Demo 2: Interactive data dashboard
Search, filter by category, and click any column header to sort. The stat cards, the bar chart, and the table all recompute from the same filtered data, instantly.
One memoised pipeline
The temptation with a dashboard is to recompute everything on every render. Instead there's a single derived pipeline, filter, then sort, wrapped in useMemo so it only reruns when an input actually changes. The summary stats and the chart derive from its output, not the raw data, so a search keystroke touches the minimum amount of work.
The search box is debounced (~180ms) so filtering doesn't fire on every keystroke, and the chart bars animate their width via CSS transitions rather than JavaScript, which keeps the main thread free.
The chart ships zero dependencies
Rather than pull in a charting library, often 50–150KB, the bar chart is hand-drawn from divs and CSS. For this shape of data it's a fraction of the bytes, fully themeable with the same CSS variables as everything else, and it inherits dark mode for free.
Knowing when not to add a dependency is part of keeping a bundle honest.
Accessible tables done properly
Sorting is exposed to assistive tech through aria-sort on each header, the sort controls are real <button>s inside <th scope="col">, and a visually-hidden role="status" region announces the result count as you filter ("7 products shown"). It reads correctly with a screen reader and works entirely from the keyboard.
The engineering principles behind both
TypeScript, everywhere.
Message roles are a union type, not a loose string; the sort key is constrained to real column names. The compiler catches the class of bug that otherwise shows up in production.
Ship less to the client.
No in-browser Babel, no charting library, Shadow DOM instead of a CSS framework. In a Next.js build this extends to Server Components and route-level code splitting, the default is to send the browser as little JavaScript as possible, which is the cheapest way to protect LCP and INP.
Accessibility is a feature, not a checkbox.
Live regions, focus management, keyboard shortcuts, and semantic HTML are in both demos from the start. It's far cheaper to build in than to retrofit.
Design that respects the reader.
Both widgets are responsive, honour prefers-color-scheme, ship a manual theme toggle, and respect prefers-reduced-motion. The visual polish is intentional, but it never comes at the cost of the fundamentals.