Fluid

Role
Solo — architecture and all three packages
Period
May — Jun 2026
Stack
TypeScript, Next.js 16, Zod, Anthropic API, Turborepo

The problem

Personalised UI doesn't scale by hand. Five intent dimensions with four values each is 1,024 archetypes — you cannot design 1,024 layouts, so teams either ship one generic interface or pay forward-deployed engineers to reshape it per customer.

What I did

Built infrastructure that generates interfaces from a single schema. Developers declare capabilities once in a .fluid.ts file; an engine reads that alongside user intent and emits a sandboxed JSON component tree. The renderer walks the tree and returns server-rendered React. No per-user component code.

What happened

Three packages — core, engine and react — split so the schema layer has no runtime dependency beyond Zod. The intermediate representation cannot reference undeclared fields or reach unauthorised APIs, and is validated before it is ever stored.

The IR is the actual product

The generation step gets the attention, but the interesting engineering is the boundary. An LLM emitting JSON that becomes a rendered interface is an injection surface unless something constrains it hard.

So the intermediate representation is a closed grammar: it can only name fields the schema declared, and it is validated by Zod before it reaches storage — not before render, before storage. Anything malformed never enters the system at all.

Two generation paths, on purpose

generate is stateless and cache-warm, for anonymous traffic and first paint. refine reads the user's stored intent profile, expands the prompt with recent history, and writes the updated intent back.

Keeping them separate meant the expensive path stays opt-in rather than becoming the default by accident.