@keepirs/react
v1.0.2
Published
React API performance monitoring SDK
Maintainers
Readme
Motivation
Tired of opening DevTools every time you need to check API performance? keepirs gives you a zero-config overlay that intercepts fetch() and shows timing, status, headers, and bodies in real time — right inside your app.
No backend required. No manual instrumentation. Just drop in <KeepirsProvider> and go.
Features
- Automatic
fetch()interception — zero manual instrumentation - 3 UI variants: island, bar, minimal
- Framework-agnostic
<KeepirsProvider>(Next.js, Vite, Remix, etc.) - Request/response headers & body inspection
- Copy as cURL
- Sensitive header masking (authorization, cookies, API keys, CSRF tokens)
- Request grouping by endpoint (opt-in) with avg/min/max latency stats
- Smart endpoint grouping (UUID, CUID, numeric ID normalization)
- Resizable detail popup with persistent sizing
- Keyboard shortcut: Cmd/Ctrl + Shift + P
- Dark / light / auto theme
- Tree-shakeable, ESM + CJS dual output
- Fully client-side — no data leaves the browser
Quick Start
npm install @keepirs/reactNext.js (App Router)
// app/layout.tsx
"use client";
import { usePathname } from "next/navigation";
import { KeepirsProvider } from "@keepirs/react";
export default function RootLayout({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
return (
<html>
<body>
<KeepirsProvider pathname={pathname}>
{children}
</KeepirsProvider>
</body>
</html>
);
}Vite / React Router
// App.tsx
import { useLocation } from "react-router-dom";
import { KeepirsProvider } from "@keepirs/react";
function App() {
const { pathname } = useLocation();
return (
<KeepirsProvider pathname={pathname}>
{/* your app */}
</KeepirsProvider>
);
}Remix
// root.tsx
import { useLocation } from "@remix-run/react";
import { KeepirsProvider } from "@keepirs/react";
export default function Root() {
const { pathname } = useLocation();
return (
<KeepirsProvider pathname={pathname}>
{/* your app */}
</KeepirsProvider>
);
}The provider handles init(), cleanup, and — when pathname is passed — clears history on route changes automatically.
Vanilla React (manual setup)
import { useEffect } from "react";
import { init } from "@keepirs/react/core";
import { KeepirsBar } from "@keepirs/react";
function App() {
useEffect(() => {
const cleanup = init();
return cleanup;
}, []);
return (
<>
{/* your app */}
<KeepirsBar />
</>
);
}Usage
<KeepirsProvider>
The recommended way to use keepirs. Wraps your app, manages the interceptor lifecycle, and clears history on route changes when pathname is provided. Provider handles data only — UI configuration is done via <KeepirsBar />.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| children | ReactNode | — | Your app content |
| filterUrl | (url: string) => boolean | — | Filter which requests to capture |
| enabled | boolean | true | Enable/disable monitoring |
| maxHistory | number | 50 | Max entries to keep in memory |
| pathname | string | — | Current route path — auto-clears history on change |
| showUI | boolean | true | Render default UI. Set false to use your own <KeepirsBar /> |
Composition Pattern
Customize the UI by placing <KeepirsBar /> as a child with showUI={false}:
<KeepirsProvider showUI={false} filterUrl={(url) => url.startsWith("/api")}>
{children}
<KeepirsBar position="bottom-right" variant="minimal" theme="dark" />
</KeepirsProvider>When inside a <KeepirsProvider>, <KeepirsBar /> automatically reads history from context — no props needed for data.
<KeepirsBar />
Standalone component (requires manual init()), or a composable child inside <KeepirsProvider> for UI customization.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| position | "bottom-center" \| "bottom-left" \| "bottom-right" | "bottom-center" | Widget position |
| variant | "island" \| "bar" \| "minimal" | "island" | UI style |
| theme | "dark" \| "light" \| "auto" | "auto" | Color theme |
| eventName | string | "keepirs:api-perf" | Custom event name to listen for |
| maxHistory | number | 50 | Max entries to keep in memory |
| pathname | string | — | Pass current route to auto-clear history on navigation |
| grouped | boolean | false | Group requests by endpoint with avg/min/max stats |
Variants
- island — Floating pill that expands into a popup list on hover. Default and recommended.
- bar — Full-width bottom bar. Good for dashboards or wide layouts.
- minimal — Small icon button. Least intrusive, opens popup on interaction.
Configuration
init() accepts an optional KeepirsConfig object:
interface KeepirsConfig {
filterUrl?: (url: string) => boolean;
enabled?: boolean;
}Examples
// Only track API routes (with KeepirsProvider)
<KeepirsProvider filterUrl={(url) => url.startsWith("/api")}>
{children}
</KeepirsProvider>
// Disable in production
<KeepirsProvider enabled={process.env.NODE_ENV !== "production"}>
{children}
</KeepirsProvider>
// Manual init with filtering
init({
filterUrl: (url) => new URL(url, location.origin).pathname.startsWith("/api"),
enabled: process.env.NODE_ENV === "development",
});Advanced
Custom Event Listening
The interceptor dispatches a CustomEvent on window for every captured request. Sensitive headers (Authorization, Cookie, X-API-Key, etc.) are masked at capture time. Build your own UI or logging on top of it:
window.addEventListener("keepirs:api-perf", (e) => {
const entry = (e as CustomEvent).detail; // PerfEntry
console.log(`${entry.method} ${entry.url} — ${entry.durationMs}ms`);
});usePerfHistory Hook
Subscribe to performance entries without any UI. Useful for building custom visualizations:
import { usePerfHistory } from "@keepirs/react";
function CustomDashboard() {
const { history, clear } = usePerfHistory({
maxHistory: 100,
pathname: location.pathname,
});
return (
<ul>
{history.map((entry, i) => (
<li key={i}>
{entry.method} {entry.url} — {entry.durationMs}ms ({entry.status})
</li>
))}
</ul>
);
}<KeepirsIsland />
Lower-level component if you manage history yourself:
import { KeepirsIsland, usePerfHistory } from "@keepirs/react";
function MyWidget() {
const { history, clear } = usePerfHistory();
return (
<KeepirsIsland
history={history}
onClear={clear}
defaultPosition="bottom-right"
defaultVariant="bar"
theme="dark"
/>
);
}Composable Exports
Build custom detail views by importing individual pieces:
import {
DetailPanel,
HeadersTable,
BodyPreview,
CopyButton,
} from "@keepirs/react";Utility Exports
import { normalizeEndpoint } from "@keepirs/react/core";
import { getDurationColor, useResizable } from "@keepirs/react";
normalizeEndpoint("/users/550e8400-e29b-41d4-a716-446655440000/posts");
// → "/users/:id/posts"
getDurationColor(1500);
// → "text-amber-500 font-semibold"API Reference
Core (@keepirs/react/core)
| Export | Type | Description |
|--------|------|-------------|
| init | (config?: KeepirsConfig) => () => void | Install fetch interceptor, returns cleanup |
| normalizeEndpoint | (url: string) => string | Normalize URL by replacing dynamic segments with :id |
| PerfEntry | interface | Shape of a captured request entry |
| KeepirsConfig | interface | Configuration for init() |
React (@keepirs/react)
| Export | Type | Description |
|--------|------|-------------|
| KeepirsProvider | Component | Framework-agnostic provider with auto-init and route awareness (data layer) |
| KeepirsBar | Component | Drop-in performance bar (standalone or inside Provider) |
| useKeepirsContext | Hook | Access perf history from Provider (throws if outside) |
| useOptionalKeepirsContext | Hook | Access perf history from Provider (returns null if outside) |
| KeepirsIsland | Component | Lower-level UI widget |
| DetailPanel | Component | Request/response detail view |
| HeadersTable | Component | Header table with sensitive masking |
| BodyPreview | Component | Pretty-printed body with copy |
| CopyButton | Component | Clipboard copy button |
| usePerfHistory | Hook | Subscribe to perf entries |
| useResizable | Hook | Pointer-drag resize with persistence |
| getDurationColor | (ms: number) => string | Duration to Tailwind color class |
| KeepirsProviderProps | interface | Props for KeepirsProvider |
| KeepirsBarProps | interface | Props for KeepirsBar |
| KeepirsPosition | type | Position union type |
| KeepirsVariant | type | Variant union type |
| KeepirsTheme | type | Theme union type |
Keyboard Shortcuts
| Shortcut | Action | |----------|--------| | Cmd/Ctrl + Shift + P | Toggle bar visibility |
How It Works
init()patcheswindow.fetchwith a timing wrapper- Each completed request dispatches a
CustomEvent("keepirs:api-perf")onwindowwith full request/response metadata - Sensitive headers (
Authorization,Cookie,X-API-Key,Proxy-Authorization,X-CSRF-Token) are masked at capture time - UI components listen for these events and render the data
- Response bodies are capped at 16KB, request bodies at 2KB
- No data is sent to any external server — everything stays in the browser
