Dev Overlay
Opt-in floating tabbed panel for surfacing development-only tooling without touching your app layout. Render any content per tab.
Request inspector
Dev-only overlay with tabs for request status (loading, error, success), logs, and live state.
A development-only inspector. Open the floating DEV control in the bottom-right (or press ⌘ / Ctrl + K) to inspect request status, logs, and live state.
default-example.tsx
1"use client";23import { useState } from "react";4import { DevOverlay } from "@/registry/ui";5import { Button } from "@/components/ui/button";67type FetchStatus = "idle" | "loading" | "success" | "error";89type DevOverlayLogEntry = {10 id: number;11 level: "info" | "warn" | "error";12 message: string;13};1415const LOG_TONE: Record<DevOverlayLogEntry["level"], string> = {16 info: "text-muted-foreground",17 warn: "text-amber-500",18 error: "text-destructive",19};2021const SAMPLE_LOGS: DevOverlayLogEntry[] = [22 { id: 1, level: "info", message: "App mounted" },23 { id: 2, level: "info", message: "Session restored from cache" },24 { id: 3, level: "warn", message: "Feature flag `beta-search` missing, using default" },25 { id: 4, level: "error", message: "Failed to prefetch /api/profile (503)" },26];2728export function DevOverlayDefaultExample() {29 const [status, setStatus] = useState<FetchStatus>("idle");30 const [payload, setPayload] = useState<Record<string, unknown> | null>(null);3132 const runRequest = (outcome: "success" | "error") => {33 setStatus("loading");34 setPayload(null);3536 window.setTimeout(() => {37 if (outcome === "success") {38 setStatus("success");39 setPayload({ user: "ada", role: "admin", tokens: 3 });40 } else {41 setStatus("error");42 setPayload(null);43 }44 }, 900);45 };4647 return (48 <div className="relative mx-auto min-h-[420px] w-full max-w-2xl space-y-3 rounded-lg border border-dashed border-border p-4">49 <p className="text-muted-foreground text-sm">50 A development-only inspector. Open the floating{" "}51 <span className="font-mono">DEV</span> control in the bottom-right (or52 press{" "}53 <kbd className="rounded border bg-muted px-1.5 py-0.5 font-mono text-xs">54 ⌘55 </kbd>{" "}56 /{" "}57 <kbd className="rounded border bg-muted px-1.5 py-0.5 font-mono text-xs">58 Ctrl59 </kbd>{" "}60 +{" "}61 <kbd className="rounded border bg-muted px-1.5 py-0.5 font-mono text-xs">62 K63 </kbd>64 ) to inspect request status, logs, and live state.65 </p>6667 <div className="flex flex-wrap gap-2">68 <Button size="sm" onClick={() => runRequest("success")}>69 Trigger success70 </Button>71 <Button size="sm" variant="outline" onClick={() => runRequest("error")}>72 Trigger error73 </Button>74 </div>7576 <DevOverlay77 enabled={process.env.NODE_ENV === "development"}78 enableHotkey79 defaultTab="request"80 tabs={[81 {82 id: "request",83 label: "Request",84 content: (85 <div className="space-y-3">86 {status === "loading" ? (87 <div className="flex items-center gap-2 text-muted-foreground">88 <span className="size-2 animate-pulse rounded-full bg-primary" />89 Loading request…90 </div>91 ) : null}9293 {status === "error" ? (94 <div className="rounded-md border border-destructive/40 bg-destructive/10 p-3 text-destructive">95 Request failed. Retry from the demo controls.96 </div>97 ) : null}9899 {status === "success" && payload ? (100 <pre className="overflow-x-auto rounded-md border border-border bg-muted/40 p-3 text-[11px] leading-relaxed">101 {JSON.stringify(payload, null, 2)}102 </pre>103 ) : null}104105 {status === "idle" ? (106 <p className="text-muted-foreground">107 No request yet. Trigger one from the demo controls.108 </p>109 ) : null}110 </div>111 ),112 },113 {114 id: "logs",115 label: "Logs",116 content: (117 <ul className="space-y-1 font-mono text-[11px] leading-relaxed">118 {SAMPLE_LOGS.map((entry) => (119 <li key={entry.id} className={LOG_TONE[entry.level]}>120 [{entry.level}] {entry.message}121 </li>122 ))}123 </ul>124 ),125 },126 {127 id: "state",128 label: "State",129 content: (130 <pre className="overflow-x-auto rounded-md border border-border bg-muted/40 p-3 text-[11px] leading-relaxed">131 {JSON.stringify({ status, payload }, null, 2)}132 </pre>133 ),134 },135 ]}136 />137 </div>138 );139}
Installation & source
Install via the shadcn CLI or copy the registry files manually.
bash
npx shadcn@latest add @tt-ui/dev-overlay
Props
| Name | Type | Default | Description |
|---|---|---|---|
| tabs | DevOverlayTab[] | Required | Each tab has a unique id, label, and content (React node) |
| enabled | boolean | false | Whether the overlay should render. Use enabled={process.env.NODE_ENV === "development"} for dev-only safety. |
| defaultTab | string | First tab id | Initially selected tab id |
| enableHotkey | boolean | false | Opt into the ⌘/Ctrl+K toggle. |
| open | boolean | undefined | Controlled open state. |
| defaultOpen | boolean | false | Initial open state for uncontrolled usage. |
| onOpenChange | (open: boolean) => void | undefined | Called when the overlay requests an open state change. |
| className | string | undefined | Applied to the FAB or the open panel shell |