Third Party Audit
A compact third-party script ledger — a totals strip (size and main-thread blocking, with a budget tick) and a sortable row list per script. Each row shows name, owner, transfer size, and a small bar of the blocking cost against the heaviest row. Drop it into a perf write-up to surface where the third-party time is going and who owns it.
Preview
Third-party scripts
Sorted by main-thread blocking. The heaviest row owns the budget.
Main-thread blocking550ms
target 200mspayload 291.4KB across 4 scripts
Sort by
- Doubleclick Ads132.4KBRevenueDisplay ad delivery240ms
- Google Analytics46.2KBMarketingPage-view + event tracking180ms
- Intercom88.7KBSupportLive chat widget95ms
- Twitter embed24.1KBEditorialTweet embeds in articles35ms
Trimming will helpThe top one or two rows dominate the cost. Talk to those owners about deferring or moving off the main thread.
Customize
Budget
200ms
Options
Installation
npx shadcn@latest add https://craftbits.dev/r/third-party-audit.jsonUsage
import { ThirdPartyAudit } from "@craft-bits/core";
<ThirdPartyAudit
scripts={[
{ id: "ga", name: "Google Analytics", owner: "Marketing", sizeKb: 46, blockingMs: 180 },
{ id: "ads", name: "Doubleclick Ads", owner: "Revenue", sizeKb: 132, blockingMs: 240 },
{ id: "intercom", name: "Intercom", owner: "Support", sizeKb: 89, blockingMs: 95 },
]}
/>Anatomy
- Header. Optional
title(rendered with thecb-labelstyle) and adescriptionsub-line. Omit both for a chromeless panel. - Totals strip. Horizontal bar gauge of total main-thread blocking with a tick at the budget. Hide it with
hideTotals. - Sort chips. Radio-group chips above the rows for
blocking,size,owner, andname. Hide them withhideSort. - Row. Name and size on the top line, optional owner and purpose underneath, a per-row blocking bar sized against the heaviest script, and an optional richer detail line.
- Verdict panel. A
role="status"strip beneath the rows whose tone tracks the run-wide verdict (good/warn/bad).
Understanding the component
- Totals. On every render the component reduces
scriptsonce into the totals (sum ofblockingMs, sum ofsizeKb, max ofblockingMs). The verdict isgoodwhen the total is undergoodBlockingMs,badat or abovebadBlockingMs, andwarnin between. - Sortable rows.
defaultSortseeds local state and the sort chip group flips it betweenblockingMs/sizeKb/owner/name. Sorting happens via a stable copy so the originalscriptsarray is preserved. - Per-row bar. Each row paints a small bar sized against
maxBlockingMs. The heaviest row picks updata-cb-heaviest="true"so consumers can re-tone it without touching the component. - Verdict styling. The root carries
data-cb-verdictfor the run-wide rating; the totals headline and verdict strip share the same hook. Override the colour by targeting[data-cb-edu="third-party-audit"][data-cb-verdict="bad"]in CSS. - Motion. Each row fades and rises in once on mount (spring
snap). The gauge fill and per-row bars use springsmooth. Every animation short-circuits underprefers-reduced-motion.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
scripts | ThirdPartyAuditScript[] | required | Scripts to audit. |
title | ReactNode | — | Optional heading above the panel. |
description | ReactNode | — | Optional sub-headline under the title. |
targetBlockingMs | number | 200 | Budget tick painted on the gauge. |
goodBlockingMs | number | 200 | Total under this rates good. |
badBlockingMs | number | 600 | Total at or above this rates bad. |
defaultSort | 'blockingMs' | 'sizeKb' | 'name' | 'owner' | 'blockingMs' | Initial sort key. |
hideSort | boolean | false | Hide the sort chip group. |
hideTotals | boolean | false | Hide the totals strip. |
headingAs | 'h2' | 'h3' | 'h4' | 'h3' | Tag for the title element. |
className | string | — | Merged onto the root via cn(). |
ThirdPartyAuditScript
| Field | Type | Description |
|---|---|---|
id | string | Stable identifier, used as React key. |
name | ReactNode | Human-readable script name. |
sizeKb | number | Transfer size in kilobytes. |
blockingMs | number | Main-thread blocking cost in milliseconds. |
owner | ReactNode | Owner / team responsible for the script. |
purpose | ReactNode | One-line purpose surfaced under the name. |
detail | ReactNode | Optional richer body shown under the bar. |
Accessibility
- The wrapper is a
<section>withdata-cb-edu="third-party-audit"andaria-describedbywired to the totals strip whenever it is visible. - The row list is a
role="list"withrole="listitem"children — assistive tech reads it as a list of scripts. - The sort chips form a
role="radiogroup"withrole="radio"per chip; keyboard users get the native radio behaviour. - The verdict strip is a
role="status"aria-live="polite"region so the rating updates as the sort or scripts change. - Reduced-motion users get static rows — no enter animation, no bar growth.
Credits
- Extracted from:
terminal-dreams(src/components/frontend-design/perf-other-assets/ui/ThirdPartyAudit.tsx). The original was wired to a project-levelassets-perf-contextengine, hard-coded a specific four-script stack, exposed per-scriptblocking/async/defer/partytownloading-mode chips, and rendered a Partytown markup snippet at the bottom. This rewrite drops the engine coupling, the loading-mode chrome, and the marketing copy — consumers pass a flatscriptsarray withsizeKbandblockingMs, and the primitive handles totals, sorting, and the impact verdict.