mcpbeat Sign in

UI Engineering Agent Skill

Use when the question is how interface should be BUILT — component boundaries, state that belongs in the URL vs the component, loading/empty/error states, forms, optimistic updates, build vs adopt. NOT the visual system (`design`), NOT framework mechanics (`react`/`nextjs`).

2k tokens
context cost
the whole folder, loaded on every use
2
files
instructions only
0
copies elsewhere
how many repositories repackaged it
105
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/ericrisco/rsc-harness --skill ui-engineering

What comes with it

2 441 bytes besides the instruction
evals/cases.yaml

The instruction itself

9 sections, as written by the author

UI engineering — the decisions between the design and the framework

There is a layer that neither the design skill nor the framework skill owns. The design says what

the screen should be. The framework says how its API works. **In between sits every decision that

makes an interface either durable or a maintenance tax** — and it is where most interface work

actually goes wrong.

Nothing here is framework-specific. These decisions are the same in React, Vue, Svelte, SwiftUI and

plain HTML; only the syntax moves.

The one rule

State goes to the outermost place that still needs it, and no further. Almost every interface

tangle is that rule broken in one of two directions: state hoarded in a component that others need,

or state hoisted into a global store that only one component ever reads.

| Where the state lives | When that is right |

| --- | --- |

| The URL | Anything a user would bookmark, share, or expect the back button to restore: filters, tabs, pagination, the open item |

| The server | Anything that outlives the session or belongs to more than one user |

| A shared parent | Two siblings genuinely need it, and it dies with the screen |

| The component | Nobody else reads it: hover, focus, an open menu, an in-flight draft |

The most common and most expensive mistake is the first row: state that belongs in the URL kept in a

component. It looks fine until someone reloads, shares a link, or presses back — and then the

interface silently loses what the user did.

Every state, not just the happy one

An interface is not finished when it renders data. It is finished when it renders all five:

| State | The question it answers | The usual failure |

| --- | --- | --- |

| Loading | "Is it working?" | A spinner where the shape is already known — use the shape |

| Empty | "Is it broken, or is there nothing?" | Blank. It must say which, and what to do next |

| Error | "What now?" | A message with no action. Every denial carries its own way out |

| Partial | "Some of it failed" | Ignored entirely; the screen shows a half-truth |

| Success | "It worked" | Silence, so the user does it twice |

The empty state is the one that gets skipped, and it is the first state a new user ever sees.

Component boundaries

A component earns its existence by hiding a decision, not by being reused. Splitting for reuse

alone produces the worst interfaces: a wrapper around a wrapper, each passing props through, none

owning anything.

Draw the boundary where:

  • it owns a piece of state nothing outside needs;
  • it can be described in one sentence without "and";
  • swapping its internals changes nothing outside it.

Do not draw a boundary because a file is long. Long is not a defect; a file that does two

unrelated things is, and it is usually short.

Forms

Forms are where interfaces most often insult people, and the rules are unglamorous:

  • Validate on blur, not on keystroke. Telling someone their email is invalid while they are on

the third character is scolding them for not having finished.

  • Never clear what they typed. Not on error, not on refresh, not on navigation. Their input is

theirs.

  • Disable the submit only while submitting, never as a way of enforcing validity — a disabled

button with no reason is a dead end.

  • The error goes next to the field, and says what to do, not what is wrong.

Optimistic updates

Show the result before the server confirms only when you can honestly undo it. That means: the

operation is idempotent, the failure is rare, and reverting it is visible and comprehensible.

Optimism on a destructive or an expensive action is a lie you will have to retract in front of the

user. Deleting, paying, sending — those wait.

Build or adopt

Adopting a component costs bundle size, an API you did not design, and an upgrade you will owe

later. Building costs the accessibility work you will get wrong.

The split is not about difficulty, it is about who is the authority on the behaviour:

  • Adopt for behaviour with a specification you would otherwise be reimplementing badly: focus

trapping, combobox semantics, date handling, virtualisation.

  • Build for anything that is your product's own logic wearing a UI, or where the library gives

you 60% and you fight the remaining 40% forever.

Anti-patterns → STOP

| If you're about to… | Reality / Fix |

| --- | --- |

| Put a filter or tab selection in component state | It belongs in the URL. Reload, share and back are part of the interface. |

| Add a global store because passing props feels tedious | Tedious is not broken. Global state that one component reads is a leak with ceremony. |

| Ship a screen with only the loaded state | Loading, empty, error and partial are not extras. The empty one is what a new user sees first. |

| Split a component because the file got long | Length is not the defect. Two unrelated responsibilities are, and they are usually short. |

| Show a delete as done before the server confirms | Optimism on a destructive action is a lie you will retract in public. |

| Reach here for how the framework's hooks work | That is ../react/SKILL.md or ../nextjs/SKILL.md. This skill decides what to build; those decide how to express it. |

See Also

  • ../design/SKILL.md — the visual system these decisions serve.
  • ../motion-craft/SKILL.md — how the pieces here move once they exist.
  • ../design-loop/SKILL.md — the graded critique that judges the result.
  • ../accessibility/SKILL.md — the ground under focus, semantics and forms.

How to use it

Copy the folder

Take ericrisco/ui-engineering from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.