The Universal Frontend Architecture: UI, Server, and State as three seams
Every frontend, in every framework, does three things: it renders a UI, it talks to a server, and it holds state. React, Vue, Angular, Svelte, and web components differ in syntax, but not in this — the three concerns are universal. The Universal Frontend Architecture is simply the decision to keep those three as separate seams rather than tangling them together. Do that, and your knowledge — and often your code’s shape — survives a framework swap, because you moved the boundaries, not the framework. Tangle them, and every concern leaks into the others, which is the state most codebases drift into by default.
Each seam has one job and a narrow interface
The UI layer renders from props and emits events — nothing more. It does not fetch, and it does not know the store exists. The State layer holds the app’s facts, exposes them via selectors, and mutates only through actions. The Server layer fetches and persists, and its results flow into State as data, never straight into a component. The interfaces between them are deliberately narrow:
// UI: pure, reads via props, reports via callbacks — swappable framework, same shape
function Cart({ items, total, onRemove }) {
return <ul>{items.map((i) => <Row key={i.id} {...i} onRemove={() => onRemove(i.id)} />)}</ul>;
}
// State ↔ Server: the effect talks to the server; data comes back as an action
const loadCart = () => async (dispatch) => {
dispatch({ type: "CART_REQUEST" });
dispatch({ type: "CART_SUCCESS", data: await api.getCart() }); // server data → state
};
The Cart component would work, essentially unchanged in spirit, in Vue or a web
component, because it only depends on the seam’s contract.
The boundaries are what survive change
The payoff is portability of reasoning. When you separate the seams, a framework migration is a rewrite of one layer’s syntax, not an archaeology dig through tangled concerns. A rendering-strategy change (CSR to SSR) touches the Server seam and leaves UI and State alone. A store swap touches State and leaves UI alone. The value is that each concern can change on its own schedule, which is exactly what a tangled codebase cannot do — there, touching the fetch means touching the component means touching the state, all at once.
Defend the seams, because they erode by default
Left alone, these boundaries dissolve: a component fetches “just this once,” a reducer grows a derived field, server data gets edited as if owned. Each is locally reasonable and globally corrosive — and an AI generating code erodes them faster, since crossing a seam is usually the shortest path. So the architecture is only real if it is defended, ideally with checks: a lint rule that forbids the UI layer importing State, a shape test on the store, a boundary the build enforces. The Universal Frontend Architecture is not a framework or a library; it is the discipline of keeping three unavoidable concerns as three separate seams, and the guardrails that keep them that way. The presentational-vs-container exercise is the UI↔State seam in practice, render-strategy-choice is the State↔Server seam, and design-micro-frontends scales the whole shape across teams.