How to transform your Lovable or Bolt MVP export into an AI-friendly codebase
You exported your Lovable or Bolt MVP — now what? The first commit feels magical: a working site in minutes, every button in place, a codebase in a zip file you “own”. But a Lovable/Bolt export is a point-in-time artifact, not a foundation. The code loads today — but if you hand it to an AI agent or refactor across pages, it’s paint on a glass window. Most tools treat export as the end; OTF treats it as the start. Here’s what you need to do to bridge from a WYSIWYG export to a codebase an AI (or a human) can really extend — and why it’s non-negotiable if you want to ship fast, fix bugs, or harness AI tooling.
The real win — what Lovable and Bolt actually nail
A Lovable or Bolt export gets what used to require a full-stack engineer's weekend: the site boots, the UI is coherent, and you have something to show. That is real value. Most tools in this bracket output readable JSX, typically with tailwind-ish classes or inline styles, and wire up a few default assets and pages. "Looks like the Figma" is a solved problem.
For the first session, you get a running project:
npm install
npm run dev
# localhost:3000 opens, and the demo works.For a landing or MVP, that's enough. But the minute you want to hand off to an AI coding agent — or scale from landing to app — friction mounts.
The pain: an export is not a platform
A sandbox export is a snapshot. Every one I’ve seen comes with the same core issues:
- Flat file structures or inconsistent folders.
- Components have inline styles or one-off tailwind classes — no theme, no design tokens.
- Logic is coupled to the page; components are not reusable slices.
- Multi-page flows are just more copies, not factored logic.
- No AI-side conventions: no CLAUDE.md, no prompts, no instructions for how tools should read the code.
- Style and structure drift with every session — the next export is just a new snapshot.
It feels like owning a house you can’t renovate: you can paint the walls, but moving the kitchen means tearing out structural supports. An agent (or even a dev two months later) has to reverse-engineer the logic from scratch. This is not a foundation.
11 production screens. Auth, DB, Stripe — all wired.
The SaaS Dashboard Kit ships everything already connected. No Vercel config, no Supabase account. Live demo at saas.otf-kit.dev.
The core thesis
A codebase your AI agent (Claude Code, Cursor, Replit) can extend safely is the opposite of a style dump — it’s built from portable components, single-responsibility files, and a design system, all wired with conventions that the agent can read. Exports hand you bricks; OTF kits hand you blueprints, scaffolding, and instructions for the next builder — human or model.
1. Flatten file structure — then name the flows, never the pixels
Almost every export delivers components/ with 50+ files, most named for looks or placement, intermixed with catch-all pages and asset folders.
The first pass is flattening:
# Before:
components/
Button.jsx
Hero.jsx
Feature1.jsx
Feature2.jsx
Pricing.jsx
SignupForm.jsx
pages/
index.jsx
signup.jsx
public/
logo.svg
img1.jpg
# After (OTF pattern):
ui/
Button.tsx
Card.tsx
PricingSection.tsx
AuthForm.tsx
flows/
signup/
logic.ts
ui.tsx
billing/
logic.ts
ui.tsx
assets/
logo.svg
hero.jpgDon’t name files for their place in the UI (“Hero2.jsx”); name them for the flow or entity (“AuthForm.tsx”, “BillingDetails.tsx”). This is how agents and humans navigate intent, not just position.
Takeaway: clear intent in file/folder names is both an affordance for humans and a survival line for AI agents that infer tasks from context.
2. Strict component boundaries — move logic out of pages
The fastest win: extract all logic from page files and push it into single-responsibility functions and hooks. Most sandbox exports leave UX events, fetches, and derived state inside the page, mixed with markup.
From the average export:
// Signup.jsx (as exported)
export default function Signup() {
const [email, setEmail] = useState("");
const handleSubmit = async (e) => { ... }
return (
<form onSubmit={handleSubmit}>...</form>
)
}To the OTF pattern:
// flows/signup/logic.ts
export function useSignupForm() {
const [email, setEmail] = useState("");
const handleSubmit = async (e) => { ... }
return { email, setEmail, handleSubmit };
}
// flows/signup/ui.tsx
export function SignupForm() {
const { email, setEmail, handleSubmit } = useSignupForm();
return (
<form onSubmit={handleSubmit}>...</form>
)
}Now an agent can find “signup logic” in one place and extend it (add a captcha, swap for Auth0) without parsing the whole page tree. Code stays testable and debuggable.
Takeaway: pull logic out of pages and into named flows — AI agents follow responsibility more than file type.
3. Replace inline styles and classes with a design system
No AI tool can propagate a new color, radius, or spacing if styles are scattered across 200 files as classes or objects. A design system turns style from entropy into structure.
The export you get:
// Hero.jsx
<div className="bg-primary text-xl px-8 py-10 rounded-xl shadow-2xl">
...
</div>What you need:
// tokens.ts
export const tokens = {
color: {
primary: '#2C3544',
secondary: '#EF4E2C',
// ...
},
radius: {
large: 24,
// ...
}
}
// Card.tsx
export function Card({ children }) {
return (
<div
style={{
background: tokens.color.primary,
borderRadius: tokens.radius.large,
boxShadow: tokens.shadow.lg,
}}
>
{children}
</div>
);
}Better: push to an npm-shipped component set (what OTF does) — so both your team and your agent can import <Card> with a set look and then override with a theme switch.
Takeaway: if the design lives in tokens and components, AI tools can update it in one move. If it’s inline, they have to guess and hope.
4. Codify conventions — write CLAUDE.md and .cursorrules
An AI coding agent does not guess what your codebase means; it reads prompts and convention files. This is the most ignored step after export.
What works:
CLAUDE.md
# Codebase Conventions
- All reusable UI lives in `ui/`
- Flows are composition of logic in `flows/<name>/logic.ts` and JSX in `flows/<name>/ui.tsx`
- Design tokens are in `tokens.ts`
- Use `Card`, `Button`, `Input` from `@otfdashkit/ui` instead of creating new primitives
- NEVER inline styles; always use tokens or UI kit components
- To add a new flow, copy the `flows/signup/` patternAI tools weight these conventions heavily. A well-structured CLAUDE.md means the agent can “add a new onboarding flow” or “globalize the theme” without breaking implicit rules.
.cursorrules
{
"components": [
{ "glob": "ui/*.tsx", "purpose": "reusable UI blocks" }
],
"flows": [
{ "glob": "flows/*/logic.ts", "purpose": "handles logic and API" }
],
"tokens": [
{ "glob": "tokens.ts", "purpose": "design tokens, no hardcoded style elsewhere" }
]
}Takeaway: conventions spelled out are instructions to the next coder — and the first AI agent that lands on your repo.
5. Port assets into structure, not snapshots
Most sandbox exports drop images and svgs into a flat public/ or assets/ folder. The problem: semantics vanish. AI agents struggle to tell which assets are used where, or to prune unused ones. Tie assets to flows or components:
assets/
logo.svg
flows/
signup/
hero-bg.jpg
illustration.svgIn OTF kits, the pattern is: global assets at root, flow-specific assets in their folder. This lets AI tools spot “dead” assets when removing or refactoring flows. The codebase self-documents the mapping.
Takeaway: tie assets to code, not just folders — it’s how models and developers avoid asset rot.

6. One design system, one look across all platforms
The familiar sandbox pattern is: “export for web, figure out mobile later”. This guarantees drift. OTF templates cut this off: a core design token set and component API drive both web and native UI (iOS/Android). So a layout change — or a new Figma theme — rolls everywhere.
Every component in OTF’s kit is:
- Identical in name and props on web, iOS, and Android
- Visually indistinguishable for a given theme (tokens flip per platform)
- Upstreamed via npm for web, and via a matching mobile package for native
One surface, one style — no side project for every device.
Takeaway: you want the component surface, not platform forks. Otherwise an AI-generated mobile version is a parallel rewrite, not an extension.
7. Durable AI affordances — every kit ships with agent-ready config
This is the part every export misses: OTF ships every template with agent affordances out of the box.
Every kit includes:
CLAUDE.mdnaming conventions and spelling out what can be extended, where to put new flows, and where not to touch..cursorrulesto help Cursor/Copilot/Claude Code auto-scope changes.- 20+ prompts and tested command recipes for agents, pre-wired to the file structure.
- One npm install from a web+mobile-native UI kit with 200+ components, so both agent and human can drop in new sections or UI flows without hunting for patterns or style guides.
This is the bridge: the agent picks up where the kit author left off, with the same rules enforced for both.
What this enables
- AI extension in minutes, not hours. An agent can add new flows, update themes, or migrate to a new API with minimal hallucination or style drift.
- Onboarding for real devs. The next engineer gets a repo with patterns, not ad-hoc decisions.
- Design updates roll everywhere. Change one token, propagate to web and mobile.
- No snapshot rot. Structure and convention mean your “exported” MVP becomes a living codebase, not a throwaway.
OTF is the bridge, not the export
Use Lovable/Bolt for your first draft. They are great at going zero-to-one. But if you want a codebase that lasts — that an AI or a human can really own — you need the layer that comes next: enforced structure, portable components with a single API on every platform, prompts and rules wired in. OTF kits aren’t just templates; they’re bridges: from export to codebase, from static UI to living product. Ship the real thing, not just the snapshot.
Ship the product, not the setup.
- 11 production screens — auth, billing, team, analytics, settings
- Real Postgres + Stripe + Better Auth, all wired on day 1
- CLAUDE.md pre-tuned so your agent extends instead of regenerates