“Use some sample data” in a prompt is an invitation to invent. The agent fabricates a user shape that almost matches production, hard-codes three pretty rows, and skips the nullables your API actually returns. Reviewers only notice when the real payload hits the page.
I’ve stopped describing sample data in prose. I point the agent at a fixture JSON file (or paste a trimmed fixture) and require the UI to consume that shape.
Why comments lose to fixtures
Prose samples drift:
- Field names get camelCased “for React” while the API is snake_case
- Optional fields disappear because the happy path didn’t need them
- Dates become
"yesterday"strings instead of ISO timestamps - Empty arrays and nulls never appear, so empty states stay theoretical
A fixture checked into src/fixtures/ or testdata/ is greppable, reviewable, and reusable in Storybook / Playwright. A comment in the prompt is gone by tomorrow.
What a good fixture looks like
Keep it boring and honest:
{ "invoices": [ { "id": "inv_1001", "number": "2026-1001", "status": "open", "customerName": "Northwind Labs", "totalCents": 129900, "currency": "USD", "dueOn": "2026-11-30", "poNumber": null }, { "id": "inv_1002", "number": "2026-1002", "status": "past_due", "customerName": "A very long customer name that should truncate in the table cell without wrapping the row awkwardly", "totalCents": 0, "currency": "USD", "dueOn": "2026-10-01", "poNumber": "PO-7781" } ], "pagination": { "page": 1, "pageSize": 25, "total": 2 } } Include at least one awkward row (long name, null, zero). That’s the point — not a marketing screenshot dataset.
Add siblings when the UI has branches:
-
invoices.empty.json→"invoices": [] -
invoices.error.json→ shape your error boundary expects -
invoices.forbidden.json→ if the UI branches on 403
Prompt shape
Build the InvoiceTable UI against the fixture at `src/fixtures/invoices.list.json`. Constraints: - Import / parse that file (or a Storybook loader that reads it) — do not invent a parallel mock object in the component file - Prop types / Zod schema must accept every field in the fixture, including nulls - Add stories: Default (list fixture), Empty (`invoices.empty.json`), LongName (already in list fixture) - If the fixture is missing a field you need, stop and propose a fixture update — do not silently invent fields in JSX - No “sample data” comments as a substitute for the file The “propose a fixture update” line keeps the contract in the data layer, where it belongs.
Wire fixtures where agents already look
- Storybook —
argsloaded from JSON, or afetchto static fixtures inpublic/fixtures - MSW / Playwright — same JSON for network stubs so E2E and stories don’t diverge
- Unit tests — import the fixture instead of inline objects
One source. Multiple consumers. The agent is less tempted to freestyle a fourth shape.
Validate the fixture once
If the repo already has Zod / JSON Schema / OpenAPI types for the endpoint, run the fixture through that parser in a one-line test:
import { invoiceListSchema } from '../api/invoiceList.schema'; import fixture from '../fixtures/invoices.list.json'; test('list fixture matches wire schema', () => { expect(() => invoiceListSchema.parse(fixture)).not.toThrow(); }); That catches the silent drift where someone “simplifies” the JSON for Storybook and breaks production parsing later. Agents should extend the fixture + schema together — never only the pretty mock.
Reject / send back
Reject
- Inline
const sampleUsers = […]that doesn’t match the fixture path you named - Happy-path-only mocks with no empty / null variants
- Renaming API fields in the UI layer “for clarity” without a mapper
- Prompt replies that say “using sample data” with no file path
Accept
- UI wired to a real fixture path
- Stories / tests that reference the same JSON
- A small fixture PR when the contract truly needs a new field
- Explicit mappers when the view model differs from the wire format
Tiny habit
Before the next UI generation prompt, drop a fixture JSON in the repo (or paste a trimmed one) and ban “sample data” wording. If the agent invents fields, don’t fix them in the component first — fix or extend the fixture, then regenerate against it. Concrete beats descriptive when the payload is the product.


