Skip to content
Framework: Vite + React + TypeScriptDifficulty: intermediate

Operations dashboard

A dark service-health dashboard with SVG sparklines, status indicators, and a filterable incident table.

A second dashboard variant focused on monitoring rather than business metrics. Each service card shows current latency, a dependency-free SVG sparkline of the last 24 samples and an error rate; the incident table filters by status. A 'simulate live updates' toggle re-samples the local data every two seconds so you can see how the UI behaves with changing values before connecting a real metrics endpoint.

  • react
  • dashboard
  • monitoring
  • charts
  • dark-mode
  • tailwind

This preview is the template's actual file tree and source code — exactly what is copied into a new project. Codexa does not publish screenshots or mock-ups of templates; run the project to see it rendered.

Files11 files

src/App.tsx113 lines
import { useEffect, useState } from "react";
import { incidents as seedIncidents, services as seedServices, tick, type Incident, type Service } from "./data";
import { Sparkline } from "./Sparkline";

const statusColor: Record<Service["status"], string> = { operational: "bg-green-500", degraded: "bg-amber-500", down: "bg-red-500" };
const incidentFilters: ("all" | Incident["status"])[] = ["all", "open", "monitoring", "resolved"];

export default function App() {
  const [services, setServices] = useState<Service[]>(seedServices);
  const [live, setLive] = useState(false);
  const [filter, setFilter] = useState<(typeof incidentFilters)[number]>("all");

  useEffect(() => {
    if (!live) return;
    const id = window.setInterval(() => setServices((s) => tick(s)), 2000);
    return () => window.clearInterval(id);
  }, [live]);

  const visibleIncidents = seedIncidents.filter((i) => filter === "all" || i.status === filter);
  const degraded = services.filter((s) => s.status !== "operational").length;

  return (
    <div className="min-h-screen bg-zinc-950 text-zinc-100">
      <header className="border-b border-zinc-800 px-4 py-3 sm:px-6">
        <div className="mx-auto flex max-w-6xl flex-wrap items-center justify-between gap-3">
          <h1 className="font-semibold tracking-tight">Operations</h1>
          <label className="flex items-center gap-2 text-sm text-zinc-400">
            <input type="checkbox" checked={live} onChange={(e) => setLive(e.target.checked)} />
            Simulate live updates
          </label>
        </div>
      </header>
      <main className="mx-auto max-w-6xl px-4 py-6 sm:px-6">
        <p className="text-sm text-zinc-400" role="status">
          {degraded === 0 ? "All systems operational." : degraded + " service" + (degraded === 1 ? "" : "s") + " degraded."}
        </p>
        <section aria-labelledby="services-title" className="mt-4">
          <h2 id="services-title" className="text-sm font-medium uppercase tracking-wide text-zinc-500">
            Services
          </h2>
          <ul className="mt-3 grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
            {services.map((s) => {
              const last = s.latencyMs[s.latencyMs.length - 1] ?? 0;
              return (
                <li key={s.name} className="rounded-xl border border-zinc-800 bg-zinc-900 p-4">
                  <div className="flex items-center justify-between">
                    <h3 className="font-medium">{s.name}</h3>
                    <span className="flex items-center gap-1.5 text-xs capitalize text-zinc-400">
                      <span aria-hidden="true" className={"size-2 rounded-full " + statusColor[s.status]} />
                      {s.status}
                    </span>
                  </div>
                  <p className="mt-3 text-2xl font-semibold tabular-nums">
                    {last}
                    <span className="text-sm font-normal text-zinc-500"> ms</span>
                  </p>
                  <Sparkline values={s.latencyMs} label={s.name + " latency over the last 24 samples"} className="mt-2 text-sky-400" />
                  <p className="mt-2 text-xs text-zinc-500">Error rate {s.errorRate.toFixed(1)}%</p>
                </li>
              );
            })}
          </ul>
        </section>

        <section aria-labelledby="incidents-title" className="mt-10">
          <div className="flex flex-wrap items-center justify-between gap-3">
            <h2 id="incidents-title" className="text-sm font-medium uppercase tracking-wide text-zinc-500">
              Incidents
            </h2>
            <div className="flex gap-1 rounded-md border border-zinc-800 p-0.5" role="group" aria-label="Filter incidents">
              {incidentFilters.map((f) => (
                <button key={f} type="button" onClick={() => setFilter(f)} aria-pressed={filter === f} className={"rounded px-2.5 py-1 text-xs capitalize " + (filter === f ? "bg-zinc-100 text-zinc-900" : "text-zinc-400 hover:text-zinc-100")}>
                  {f}
                </button>
              ))}
            </div>
          </div>
          <div className="mt-3 overflow-x-auto rounded-xl border border-zinc-800">
            <table className="w-full min-w-[560px] text-sm">
              <thead className="bg-zinc-900 text-left text-zinc-400">
                <tr>
                  <th className="p-3">ID</th>
                  <th className="p-3">Title</th>
                  <th className="p-3">Service</th>
                  <th className="p-3">Status</th>
                  <th className="p-3">Opened</th>
                </tr>
              </thead>
              <tbody>
                {visibleIncidents.map((i) => (
                  <tr key={i.id} className="border-t border-zinc-800">
                    <td className="p-3 font-mono text-xs text-zinc-400">{i.id}</td>
                    <td className="p-3">{i.title}</td>
                    <td className="p-3 text-zinc-400">{i.service}</td>
                    <td className="p-3 capitalize">{i.status}</td>
                    <td className="p-3 text-zinc-400">{new Date(i.openedAt).toLocaleString()}</td>
                  </tr>
                ))}
                {visibleIncidents.length === 0 ? (
                  <tr>
                    <td colSpan={5} className="p-6 text-center text-zinc-500">
                      No incidents with this status.
                    </td>
                  </tr>
                ) : null}
              </tbody>
            </table>
          </div>
        </section>
      </main>
    </div>
  );
}

Dependencies

Documented from the template's package.json. The runtime has a prebuilt dependency set for Vite + React + TypeScript, so installs are fast.

PackageVersion
react^19.1.0
react-dom^19.1.0
vite^6.3.0
@vitejs/plugin-react^4.4.0
tailwindcss^4.1.0
@tailwindcss/vite^4.1.0
typescript^5.8.0

How it runs

Framework
Vite + React + TypeScript
Dev command
npm run dev
Build command
npm run build
Output directory
dist
Port
5173
Opens at
/
Difficulty
intermediate

Projects run in an isolated sandbox with a live preview. Read the projects documentation for details on settings, snapshots and deployment.

View all
  • Framework: Vite + React + TypeScriptDifficulty: intermediate
    Featured

    Analytics dashboard

    A responsive dashboard layout with sidebar navigation, stat cards, and a data table.

    • react
    • dashboard
    • tailwind
    • table

    9 files

Need the raw data? GET /api/v1/templates/vite-react-ops-dashboard returns this template with its files.

Build on Operations dashboard

Create a free account, start from this template, and ask the AI agent to turn it into your app.