Engineering

Building the Dashboard Without a Framework

Part three of building oo-workers in public: why the admin dashboard is a hand-rolled vanilla-TypeScript SPA with no React, and what the v2 redesign added.

ObserveOne Team
3 min read

In this series

Building oo-workers in public

  1. 1.How We Built a Self-Hosted Monitoring Engine
  2. 2.Eight Monitor Types From One Engine
  3. 3.Building the Dashboard Without a Framework
  4. 4.A Portable Backup Format, Not pg_dump
  5. 5.A Three-Pass Code Audit in 14 PRs
  6. 6.Killing the 5-Second Poll
  7. 7.Cutting the Docker Image to 1.4 GB
  8. 8.Tests That Spin Real Databases
  9. 9.The Alert That Never Fired

By the time the engine ran eight monitor types, it needed a real dashboard. We built one with no frontend framework: a hand-rolled vanilla-TypeScript single-page app, bundled by Bun, shipped inside the same container as the worker.

oo-workers self-host dashboard showing the monitor list, availability banner, and stat cards
The dashboard: a fixed navbar, a fleet-availability banner, stat cards, and the monitor list tabbed by type.

Why no framework#

oo-workers is a self-host product. The dashboard ships in the container next to the worker, so every dependency it carries is weight in the image and another thing to keep current. A framework would add a build toolchain, a runtime, and a churn cycle, to render what is ultimately an admin panel: operators glance at it, they do not live in it. Keeping it dependency-light meant a smaller image and a faster load, with nothing to upgrade between releases.

How it works#

The whole UI is one app.ts that Bun bundles for the browser. Routing is hash-based, with a render function per view: list, detail, regions, channels, status pages, settings, incidents. Each renders its own markup; navigating swaps the view. There is no virtual DOM, because the views are simple enough that re-rendering on navigation costs nothing noticeable. Styling is a set of CSS tokens plus one stylesheet.

Public status pages are deliberately not part of this. They are server-rendered and anonymous, so a visitor checking your status loads plain HTML with no app to boot.

The v2 redesign#

Version 1.16.0 was when the hand-rolled UI got real structure. It added a fixed navbar, a sectioned add-monitor dialog, slide-over create flows, a Settings page (profile, security, API keys, backup), an incidents list and editor, a dashboard active-incidents widget with a fleet-availability sparkline, brand-icon alert channels, and a reskinned login and setup flow. All of it built from the same plain primitives, no framework pulled in to get there.

Add-monitor dialog with a sections rail and type tiles grouped by category
The sectioned add-monitor dialog, with monitor types grouped into HTTP, network probes, and push-based.

The trade-off#

No framework means no component ecosystem. You write your own dialog and slide-over primitives, and deeply interactive state would eventually justify reaching for one. For a monitoring admin panel, it has not, and the payoff is a UI that adds almost nothing to the image and never breaks on a framework upgrade.

Ready for AI-Powered Testing?

ObserveOne monitors your selectors 24/7 and automatically heals them when websites change. Never deal with broken tests again.

Start Free Trial