“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 — args loaded from JSON, or a fetch to static fixtures in public/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.