All components

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.

1"use client";
2
3import { useState } from "react";
4import { DevOverlay } from "@/registry/ui";
5import { Button } from "@/components/ui/button";
6
7type FetchStatus = "idle" | "loading" | "success" | "error";
8
9type DevOverlayLogEntry = {
10 id: number;
11 level: "info" | "warn" | "error";
12 message: string;
13};
14
15const LOG_TONE: Record<DevOverlayLogEntry["level"], string> = {
16 info: "text-muted-foreground",
17 warn: "text-amber-500",
18 error: "text-destructive",
19};
20
21const 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];
27
28export function DevOverlayDefaultExample() {
29 const [status, setStatus] = useState<FetchStatus>("idle");
30 const [payload, setPayload] = useState<Record<string, unknown> | null>(null);
31
32 const runRequest = (outcome: "success" | "error") => {
33 setStatus("loading");
34 setPayload(null);
35
36 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 };
46
47 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 (or
52 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 Ctrl
59 </kbd>{" "}
60 +{" "}
61 <kbd className="rounded border bg-muted px-1.5 py-0.5 font-mono text-xs">
62 K
63 </kbd>
64 ) to inspect request status, logs, and live state.
65 </p>
66
67 <div className="flex flex-wrap gap-2">
68 <Button size="sm" onClick={() => runRequest("success")}>
69 Trigger success
70 </Button>
71 <Button size="sm" variant="outline" onClick={() => runRequest("error")}>
72 Trigger error
73 </Button>
74 </div>
75
76 <DevOverlay
77 enabled={process.env.NODE_ENV === "development"}
78 enableHotkey
79 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}
92
93 {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}
98
99 {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}
104
105 {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

NameTypeDefaultDescription
tabsDevOverlayTab[]RequiredEach tab has a unique id, label, and content (React node)
enabledbooleanfalseWhether the overlay should render. Use enabled={process.env.NODE_ENV === "development"} for dev-only safety.
defaultTabstringFirst tab idInitially selected tab id
enableHotkeybooleanfalseOpt into the ⌘/Ctrl+K toggle.
openbooleanundefinedControlled open state.
defaultOpenbooleanfalseInitial open state for uncontrolled usage.
onOpenChange(open: boolean) => voidundefinedCalled when the overlay requests an open state change.
classNamestringundefinedApplied to the FAB or the open panel shell