@apitraffic/embed-react
React components for embedding ApiTraffic request logs in React applications. Provides both a drop-in component and a headless hook for full rendering control.
Built on top of @apitraffic/embed (Web Components with Shadow DOM).
Installation
npm install @apitraffic/embed-react @apitraffic/embed
yarn add @apitraffic/embed-react @apitraffic/embed
pnpm add @apitraffic/embed-react @apitraffic/embed
Peer dependencies: react >= 16.8.0 and react-dom >= 16.8.0
Ships ESM and CommonJS builds with TypeScript declarations (@types/react is an optional peer, for TypeScript users). Both exports are client components: the build starts with 'use client', and importing the package on a server is safe.
Quick Start
1. Generate an embed token (server-side)
Your backend requests a signed embed token from the ApiTraffic API. The token locks in which bucket and filters the embed can access.
app.get('/api/embed-token', requireUser, async (req, res) => {
const response = await axios.post(
`https://api.apitraffic.io/v1/accounts/${ACCOUNT_SID}/embeds/token`,
{
bucketSid: 'your-bucket-sid',
filters: {
environmentSid: 'abc123',
criteria: 'method == "GET"'
},
ttl: 3600,
theme: 'light',
tabs: ['request', 'response', 'connection', 'tags', 'ai'],
features: {
copyRequest: true,
includeBodies: false
}
},
{ headers: { Authorization: `Bearer ${API_TOKEN}` } }
);
res.json(response.data);
});
2. Use in React
import { RequestLog } from '@apitraffic/embed-react';
function Dashboard() {
const [token, setToken] = useState(null);
useEffect(() => {
fetch('/api/embed-token')
.then(res => res.json())
.then(data => setToken(data.token));
}, []);
const handleTokenExpired = async () => {
const res = await fetch('/api/embed-token');
const data = await res.json();
return data.token;
};
if (!token) return <div>Loading...</div>;
return (
<RequestLog
token={token}
theme="light"
pageSize={25}
onTokenExpired={handleTokenExpired}
onReady={() => console.log('Embed loaded')}
onError={(e) => console.error('Embed error:', e.detail)}
onRequestClick={(e) => console.log('Clicked:', e.detail.requestSid)}
/>
);
}
<RequestLog> Component
A React wrapper around the <apitraffic-request-log> Web Component. Renders inside Shadow DOM for full CSS isolation.
- Server rendering: renders the bare element on the server; the Web Component is loaded with a dynamic
import() in an effect, so nothing browser-only runs during SSR.
- The token stays out of the DOM: it is set on the element as a property, never rendered as an attribute, so it is in neither the server HTML nor the live DOM.
Props
token | string | — | Required. The signed embed JWT. |
theme | string | 'light' | 'light', 'dark', or 'auto' (follows OS). |
apiUrl | string | 'https://api.apitraffic.io/v1' | Override the API base URL. |
pageSize | number | 25 | Requests per page (1–100). |
showDetail | boolean | true | Enable/disable expandable row detail. |
onTokenExpired | function | — | Async callback — return a new token string to refresh. Never called concurrently; one that has not settled within 30 s counts as failed. |
onReady | function | — | Called after the first successful data load. |
onError | function | — | Called on API errors, a failed onTokenExpired (detail.code: 'TOKEN_REFRESH_FAILED') an expired token handed back ('TOKEN_EXPIRED') or a failure to load the component ('EMBED_LOAD_FAILED'). Receives the CustomEvent. |
onRequestClick | function | — | Called when a row is clicked. e.detail.requestSid. |
className | string | — | CSS class applied to the host element. |
style | object | — | Inline styles applied to the host element. |
Token Auto-Refresh
The onTokenExpired callback fires ~30 seconds before the JWT expires, or when the API rejects it (a 401). A visitor's clock is corrected for only when the token's iat proves it wrong (see the @apitraffic/embed README); a clock that is somewhat fast just makes the callback fire that much early, never in a loop, and repeated rejections back off from 1 s to 5 minutes. A callback that has not settled within 30 s counts as failed (TOKEN_REFRESH_FAILED), so it cannot block later refreshes. Return a new token string to seamlessly refresh — the returned token and the token prop feed the same state, and a later prop change wins (a refresh that settles after the token prop has changed is ignored). Returning the same token once it has expired shows "Token refresh returned an expired token." and calls onError with detail.code: 'TOKEN_EXPIRED':
<RequestLog
token={token}
onTokenExpired={async () => {
const res = await fetch('/api/embed-token');
const { token: newToken } = await res.json();
return newToken;
}}
/>
Theming
Style the component via CSS custom properties on a wrapper or globally:
apitraffic-request-log {
--at-font-family: 'Inter', sans-serif;
--at-bg-color: #ffffff;
--at-text-color: #1f2937;
--at-accent-color: #3b82f6;
--at-border-color: #e5e7eb;
--at-border-radius: 6px;
--at-row-hover-bg: #f9fafb;
--at-max-height: 600px;
}
Or set theme="dark" / theme="auto" for built-in dark mode.
Tabs
Which request detail tabs the embed shows is decided by the token, not by props — see the @apitraffic/embed README for the full list. Set tabs when minting the token; the API withholds the data behind every tab you leave out, so there is nothing for a viewer to un-hide in the DOM. onReady receives the resolved list as e.detail.tabs and e.detail.features.
useApiTrafficEmbed Hook
A headless hook that fetches embed data and returns raw state — you render your own UI. No Web Component, no Shadow DOM: it imports only @apitraffic/embed/headless, so no custom element is registered.
Every request is cancelled with an AbortController when it stops being wanted (unmount, a new token, apiUrl or pageSize), a late answer for an old token is dropped, and no state is set after unmount. A new token (including one returned by onTokenExpired), apiUrl or pageSize clears requests, config, hasMore, error and loadingMore in the very render that brings it (loading reads true there), so neither rows nor an error from one token are ever shown under another, even if the new load fails; only refresh() keeps the current rows on screen while it reloads. Clearing token clears requests. A token onTokenExpired returns is dropped if the token has changed since the refresh started, so a late answer for a previous token never overrides a newer token prop. If it returns the same token and that token has expired, error reads "Token refresh returned an expired token." rather than the list sitting empty. A loadMore kept from an earlier render (an observer that captured it once) does nothing once its token, apiUrl or pageSize is no longer current; use the one from the latest render.
import { useApiTrafficEmbed } from '@apitraffic/embed-react';
function CustomRequestLog({ token }) {
const {
requests,
loading,
loadingMore,
error,
hasMore,
loadMore,
refresh
} = useApiTrafficEmbed({
token,
pageSize: 50,
onTokenExpired: async () => {
const res = await fetch('/api/embed-token');
const { token } = await res.json();
return token;
}
});
if (loading) return <Spinner />;
if (error) return <Alert>{error}</Alert>;
return (
<div>
<button onClick={refresh}>Refresh</button>
<table>
<thead>
<tr>
<th>Method</th>
<th>Path</th>
<th>Status</th>
<th>Duration</th>
</tr>
</thead>
<tbody>
{requests.map(req => (
<tr key={req.sid}>
<td>{req.request?.method}</td>
<td>{req.request?.path}</td>
<td>{req.response?.statusCode}</td>
<td>{req.timings?.duration}ms</td>
</tr>
))}
</tbody>
</table>
{hasMore && (
<button onClick={loadMore} disabled={loadingMore}>
{loadingMore ? 'Loading...' : 'Load More'}
</button>
)}
</div>
);
}
Hook Options
token | string | — | Required. The signed embed JWT. |
apiUrl | string | 'https://api.apitraffic.io/v1' | Override the API base URL. |
pageSize | number | 25 | Requests per page. |
onTokenExpired | function | — | Async callback — return a new token. Called before expiry and on a 401; never concurrently. A rejection, or no answer within 30 s, sets error. |
Hook Return Value
requests | array | Array of request records. |
config | object | The token's resolved { view, theme, tabs, features } — records only carry the fields those tabs allow. |
loading | boolean | true during initial load. |
loadingMore | boolean | true while fetching the next page. |
error | string | Error message, or null. |
hasMore | boolean | Whether more pages are available. |
loadMore | function | Call to fetch the next page. |
refresh | function | Call to reload from the beginning. |
Security
- Embed JWTs are signed server-side with per-account HMAC-SHA256 secrets.
- Filters are locked into the token — the client SDK cannot modify them.
- Exposed tabs and features are locked into the token too, and enforced server-side: responses are filtered down to the fields the granted tabs need.
- Tokens are short-lived with automatic refresh support.
- Embed data endpoints only accept embed JWTs — standard API/session tokens are rejected.
License
MIT