# With AI, You Barely Need a Frontend Framework

dlants's April 2026 post argues that when an LLM writes most of your frontend code, framework sophistication becomes a liability. React's abstractions (VDOM, hooks, reconciliation, concurrent rendering) reduce human repetition at the cost of implicit behavior that LLMs can't reliably reason about. If the agent is generating the code, repetition is free — what you need instead is explicitness, locality, and predictability. The result is [[vamp]], a minimal TypeScript pattern that's "barely a frontend framework."

## The pattern

Every view implements the same shape:

```typescript
interface View<State, Message> {
  container: HTMLElement;
  sync(state: State): void;
  destroy(): void;
}
```

State flows one direction: DOM event → dispatch → `update()` mutates state → `view.sync()` pushes changes to the DOM. There's no virtual DOM, no diffing, no reconciliation. `sync()` walks through explicit bindings (`bindText`, `bindAttr`, `bindClass`, `bindVisible`) and sets DOM properties directly. Each binding knows which element it owns and what state field it watches.

Composition works through `bindChild` (static child mounting), `bindSlot` (conditional mount/unmount of child views based on state), and `bindList` (keyed array reconciliation). Parent views wrap child messages into discriminated unions for type-safe communication. Every DOM reference uses a generated unique ID from `ref()`, preventing selector collisions between components.

CSS is handled by `cls()` and `mountStyle()` — generate a unique class name, inject the CSS string, no build tooling. The entire framework is one TypeScript file (`vamp.ts`) that you copy into your project.

## Why LLMs like this pattern

The core insight is about what makes code LLM-friendly versus human-friendly. Human-oriented frameworks minimize repetition because typing is expensive and humans get bored. AI-oriented patterns minimize *implicit behavior* because LLMs can't reason about rules they can't see in context.

React's implicitness is well-documented in the post's appendix:

- **Reconciliation** is invisible. Changing a conditional branch can silently remount a component, destroying input focus and local state. The developer never asked for a remount — React's diffing algorithm decided one was necessary. An LLM generating code inside a conditional has no way to know this will happen without understanding React's reconciliation algorithm, which isn't in the code.
- **Hooks** depend on call order. Every `useState` and `useEffect` must execute in the same order on every render. Put a hook inside an `if` and it breaks. Dependency arrays are another implicit contract — miss a dependency and you get stale closures. LLMs frequently get this wrong because the rules are positional and invisible.
- **Concurrent rendering** introduces tearing (reading different state versions within one render), timing surprises (effects don't run when you expect), and measurement problems (the DOM isn't ready when `useLayoutEffect` fires in concurrent mode). None of this is visible in the code.
- **VDOM diffing** requires manual optimization (`React.memo`, `shouldComponentUpdate`, Immer for immutability) to avoid unnecessary re-renders. The default behavior is "re-render everything and diff," which is correct but slow.

Vamp has none of these. Each view's `sync()` method is a flat list of explicit DOM mutations. There's no implicit re-render, no hook ordering, no dependency array, no diffing. An LLM reads one view, sees the pattern, and produces the next view by repeating the same structure with different bindings. The repetition that would bore a human developer is invisible to an LLM — generating 50 lines of `bindText` calls costs the same as generating 5.

## The trade-off

The post doesn't pretend vamp is better than React for human teams. React's abstractions exist because they solve real problems at scale — component reuse, performance optimization, ecosystem compatibility. Vamp trades all of that for LLM legibility. You lose the ecosystem (no component libraries, no React DevTools, no Next.js), you lose the abstraction power (no custom hooks, no context providers), and you get back a pattern that's trivial for an LLM to produce correctly on the first try.

This is a specific instance of the broader argument in [[clean-code-coding-agents]]: code structure should optimize for whoever reads it, and if agents read it more often than humans, optimize for agents. It's also the constructive answer to [[cult-of-vibe-coding]] — Cohen says read the code yourself; vamp says write the code so it's readable by both you and the agent, by making every behavior explicit.

The connection to [[no-silver-bullet-llms]] is more subtle. Bennett argues the bottleneck is design, not code generation. Vamp doesn't challenge that — you still design the view hierarchy, the state shape, and the message types yourself. What vamp does is make the *code generation* part as reliable as possible by removing the framework-specific footguns that cause agents to produce subtly wrong output. It attacks a narrow accidental difficulty (framework implicit behavior confusing LLMs) rather than the essential difficulty of frontend design.
