Components Are Contracts, Not Files Ask a mid-level developer to build a component and they open a file and start writing markup. Ask an architect and they pause on a different question first: what is this component's contract with the rest of the app? That reframe is the whole difference, and it's worth making concrete right away. A component's markup is private — you can rewrite it whenever you like, and nobody outside notices. But the moment another part of the app uses your component, its inputs, outputs, and slots become a public API you can't change without breaking every caller. The internals are cheap. The contract is expensive. So design the contract first, before writing a single line of the template. Here are the four questions that define it. 1. What does the caller pass in, and what comes back out? Inputs and outputs are the signature of your component. Keep them minimal and intention-revealing. A card that takes one well-typed object beats a card with eleven loose props: — one input, one clear meaning. — eleven promises you now have to keep forever. Every prop you add is a commitment. It's another thing a caller can get wrong, and another name you can never rename without a coordinated migration. When you find the input list growing, ask whether several props are really one object trying to get out. 2. Who owns the state — the component, or the caller? This is the controlled vs uncontrolled decision, and it's one people make by accident instead of on purpose. A controlled component takes its value from the parent and reports changes back (a in, a change event out). The parent owns the truth. An uncontrolled component manages its own state internally and just tells you when something happened. There's no universally correct choice — but there is a wrong move, which is being vague about it. If it's unclear who owns the state, callers end up either duplicating it or fighting the component for control. Pick one deliberately, and make it obvious from the API. 3. Where does customization go — props or slots? Watch for this smell: you add , then , then , then . Four props, all trying to describe one thing the caller could have just handed you . That's the signal to reach for a slot instead of more props. Props configure; slots compose. Let the caller supply the shape, rather than toggling your guesses about it. In Angular Use content projection with : In React The same idea is — or a node-typed prop when you need a named region: Either way, the rule underneath is the same: props are you predicting every customization the caller might want; slots let the caller own it. Slots scale; prop-explosions don't. The framework changes the syntax, not the decision. 4. Can you swap the internals later without the caller noticing? This is the test of a clean contract. If you can replace what's inside the component without any caller changing, the contract is honest. If changing your rendering library forces every caller to change too, the implementation has leaked into the API. The classic example is wrapping a third-party component. Suppose you're using a powerful data grid. The tempting shortcut is to accept the library's own config object straight through your props: The day you want to replace that grid, every caller breaks, because they were all speaking the library's language. Instead, expose your contract and keep the library private behind it: Now the grid is an implementation detail. Replacing it is a change inside one file, not a migration across the codebase. (There's a whole lesson on this later in the chapter — "Build, Buy, or Wrap" — but the principle starts here, in the contract.) The Trade-off: Is Contract-First Design Worth the Upfront Thought? Designing the contract before the internals takes longer on day one than just opening the file and writing markup. Whether that's worth it depends entirely on what happens after day one. The pain, concretely Nobody sets out to build a bad contract. It happens one convenient shortcut at a time: v1: takes , , — three props, written for exactly one screen. Fast to ship. v2: A second screen needs an online indicator. Rather than pause and redesign, you add . Still feels fine — one more prop. v3: A third screen needs the card to be clickable sometimes and static other times. You add and start checking inside the render to decide whether to add cursor styling and a hover state — logic that's now implicit and untested. v4: Someone needs to change how the avatar renders — swap a raw for a lazy-loading component. Half the callers were relying on the old element structure for their own CSS overrides that reached into 's internals. The "internal" change breaks five unrelated screens, and nobody remembers why until they git-blame each one. By v4, isn't a component with a contract — it's a pile of props whose valid combinations exist only in the heads of whoever wrote each call site. Side-by-side No explicit contract Contract-first design --- --- --- Adding a new prop…