Skip to content
cd ..

Building a React Dashboard Without a Build Step

// · 4 min read

The AgenticMail Enterprise admin dashboard is a single HTML file. No build step. No bundler. No node_modules folder consuming half your disk. Just one file that loads React 18 and ReactDOM from a CDN, with every component defined inline. And it works beautifully.

Why No Build Step?

The dashboard needed to ship inside a self contained deployment. AgenticMail Enterprise runs in environments where customers don’t want to install Node.js, don’t want to run npm install, and definitely don’t want to troubleshoot webpack configuration conflicts. The admin interface had to be something you could serve from any static file server, embed in an existing app, or just open from disk.

A single HTML file solves all of that. Copy it anywhere. Serve it from anything. No dependencies to install, no build artifacts to manage, no CI pipeline required just to change a button color.

The Scale of It

This isn’t a toy dashboard. It’s 28 distinct pages covering agent management, email routing, analytics, system health, configuration, soul template editing, autonomy settings, credential vault management, audit logs, and more. Each agent has a detail view with 23 tabs: overview, email history, performance metrics, active goals, communication log, tool usage, knowledge base, schedule, escalation history, and others.

All of that lives in one file. Components are defined as regular functions using React.createElement calls (no JSX since that would require a transpiler). State management uses React’s built in useState and useReducer hooks. Routing is handled by a lightweight hash based router written in about 40 lines.

How It Actually Works

The HTML file loads three external scripts from CDN: React, ReactDOM, and optionally a charting library for the analytics views. Everything else is inline JavaScript.

Each “page” is a function component. Navigation updates the URL hash, and a top level router component reads that hash to decide what to render. Data fetching uses the standard fetch API, hitting the same Hono API server that powers everything else.

Styling uses a combination of inline styles and a single style block at the top of the file. I adopted a utility pattern similar to Tailwind’s philosophy but implemented as JavaScript style objects. A styles namespace at the top of the script defines reusable style fragments that components compose together.

The Tradeoffs

Let’s be honest about what you lose. No JSX means writing React.createElement('div', { className: 'panel' }, children) instead of <div className="panel">{children}</div>. It’s more verbose. You get used to it faster than you’d think, but it’s definitely more characters per component.

No TypeScript means you rely on discipline and good naming instead of the compiler catching your mistakes. For a dashboard that’s primarily read/display with straightforward data shapes, this turned out to be less painful than expected.

No hot module replacement means you refresh the browser manually during development. An old school workflow, but the page loads in under a second, so the feedback loop is still tight.

No tree shaking means every component ships whether or not the user visits that page. At the scale of this dashboard, the total JavaScript is still under 200KB, which is smaller than most bundled React apps after tree shaking anyway.

What You Gain

Deployment is trivially simple. The dashboard is served by the same Hono server that runs the API. One route, one file, done. Updates ship by replacing a single file. There’s no build cache to invalidate, no chunk hashing to worry about, no CDN purge to trigger.

Debugging is straightforward because what you see in the browser’s source view is exactly what you wrote. No source maps needed. No mapping between transpiled output and original code.

The entire dashboard can work offline after the initial CDN scripts are cached. In air gapped enterprise environments, you swap the CDN URLs for local paths and it just works.

Would I Do It Again?

For this specific use case, absolutely. The constraints of enterprise deployment made a build step free approach not just viable but preferable. The dashboard is fast, maintainable, and deploys anywhere without fuss.

For a larger team or a more complex frontend with heavy interactivity, I’d probably reach for a proper build pipeline. But for an admin dashboard that prioritizes reliability and deployment simplicity over developer ergonomics, a single HTML file is hard to beat.

Sometimes the best toolchain is no toolchain at all.

Source Code

View the full source on GitHub

// share

// subscribe

New posts and updates straight to your inbox. No noise.

cd ..