AdaptiveUI: AI-powered adaptive search
A chatbot that decides how to present each answer (cards, charts, tables, code and 20+ other component types) and renders that interface in real time from structured LLM output.
- Client
- Personal product
- Role
- Sole engineer — design, front end, API and AI integration
- Published
- 18 May 2026
Impact
- UI component types
- 25
- Models in fallback registry
- 10 across 2 providers
- Attempts before failing
- 3

The problem
Chatbots answer everything as a wall of text. A product comparison wants a card grid, a trend wants a chart, an implementation question wants highlighted code. The interface should adapt to the answer, not the other way round.
The solution
The model is prompted to act as a JSON API that returns a list of typed UI components, not prose. The front end maps each of 25 component types (cards, charts, tables, timelines, code, quizzes and more) to a React renderer. Every query opens in its own tab so answers can be compared.
AI acceleration
AI tooling drafted renderers and boilerplate quickly, which freed time for the parts that decide whether an LLM product works: the output schema, the fallback chain across models, and handling quota and malformed-response failures.
How it works#
A question goes to a single POST /api/chat Route Handler. The handler validates the input, rejecting empty queries and anything over 2,000 characters, then asks the model for a JSON object describing the UI to render.
// app/api/chat/route.ts (simplified)
if (!body.query || body.query.trim().length === 0) {
return NextResponse.json({ error: "Query is required" }, { status: 400 });
}
if (body.query.trim().length > 2000) {
return NextResponse.json({ error: "Query must be 2000 characters or fewer" }, { status: 400 });
}
const adaptiveResponse = await generateAdaptiveUI(body.query.trim(), body.history ?? []);
return NextResponse.json(adaptiveResponse);Making LLM output dependable#
Free-tier models fail in different ways: quota errors, rate limits, timeouts and invalid JSON. Instead of depending on one model, AdaptiveUI keeps a ranked registry of 10 models across Gemini and OpenRouter, grouped into flagship, strong-free and lite tiers. Each query works down the list, trying up to three models before giving up, so a single provider outage or quota limit doesn't take the product down.
What I'd do next#
- Stream components as they are generated, so the first card appears before the whole response is ready.
- Validate the component schema with Zod at the API boundary and retry once on malformed output.
- Cache popular queries at the edge.