A self-hostable replacement for Lovable ($25/mo). Describe a website, watch it get built page by page, refine it by chatting, export it. Runs on a free hosted model, or fully offline on your own machine.
Drop #001 of Slopsource. Not affiliated with Lovable.
A real site built by gemma-4-26b-a4b:free — chat and history on the left, live preview on the right, export and deploy top-right.
cp ../.env.example ../.env
cd unlovable && docker compose upOpen http://localhost:4321. The app asks you to connect a model on first run — pick a provider, paste a free key, done. No file editing.
Without Docker:
npm install && npm run devFree tiers have a daily request cap. A 5-page site costs 6 requests (one plan, one per page). On OpenRouter's free tier that works out to roughly 8 sites a day before you get:
429 Rate limit exceeded: free-models-per-day
That is the provider's limit, not a bug, and the app shows you the message. If you hit it: switch provider in Settings, run locally, or add credits with whoever you're using.
Running locally needs one non-obvious flag:
OLLAMA_CONTEXT_LENGTH=16384 ollama serveOllama defaults to 4096 tokens. Every chat edit sends the whole site, which overflows that and is truncated silently — editing looks completely broken while generation looks fine. This cost an hour to diagnose.
- Describe a site in a sentence → a complete multi-page static website
- Watch each page get written, one at a time
- Chat to refine: "make the headline punchier", "add a pricing section"
- Pick your model from the free ones your provider offers
- Full version history, one-click rollback
- Reopen anything you've built before
- Export a
.zipof dependency-free HTML and CSS - Deploy to Netlify in one click, if you add a token
Up front, because finding out later is worse.
- Static sites only. Marketing pages, portfolios, landing pages. No React apps, no backend, no database, no forms that submit anywhere.
- No images. Pages are built from type, colour and layout. There is no image generation and no stock library.
- Single user, no auth. It's your machine. Don't expose it to the internet.
- The model writes structure, not CSS. Design comes from tokens rendered by code, which is why separately-generated pages look like one site. You get a coherent site, not an arbitrary one.
- A page containing a literal
=======line can break editing. That's the delimiter of the SEARCH/REPLACE format the edit loop uses; aider has the same limitation. - Max 5 pages per site. More pages means more sequential model calls, and that gets slow fast.
- Free models vary a lot. Some advertised as free return nothing at all. The picker only lists ones your provider actually offers, but it can't tell you which are good — see the table below.
Measured, not estimated.
Hosted — one full page, same prompt, via OpenRouter:
| Model | Time | Finished cleanly |
|---|---|---|
google/gemma-4-26b-a4b-it:free ← default |
62s | ✅ |
poolside/laguna-s-2.1:free |
112s | ✅ |
openai/gpt-oss-20b:free |
135s | ✅ |
inclusionai/ling-3.0-flash:free |
15s | ❌ truncated |
nvidia/nemotron-3-ultra-550b-a55b:free |
219s | ❌ truncated |
Of the 17 models OpenRouter lists as free, 8 actually answer. cohere/north-mini-code:free sounds ideal and returns empty. Reasoning models are the usual failure: they spend the token budget thinking and get cut off before the answer. The app rejects truncated output rather than handing you half a page — raise LLM_MAX_TOKENS or pick another model.
Local — qwen2.5-coder:7b on an Apple Silicon laptop, fully offline:
| Step | Time |
|---|---|
| Site plan | 22s |
| Each page | 23–39s |
| One chat edit | 10–19s |
| Export | 0 remote references |
A 7B model is the floor and it clears it: valid plan JSON, no <style> tags, no markdown fences leaking through, working nav links.
The interesting part is what the model isn't asked to do.
- Plan — one small call returns JSON: the page list plus seven design tokens. The response is scanned for every balanced JSON value and the last one that parses wins, so a model that shows an example before its real answer doesn't poison the result.
- Stylesheet — rendered by TypeScript from those tokens. The model never writes CSS. This is why separately-generated pages look like one site, and why exports have no CDN dependency. Tokens that would inject CSS or fetch something remote are rejected.
- Pages — one bounded call per page, writing semantic HTML against a fixed class vocabulary the stylesheet is guaranteed to define. Nothing depends on a single long generation succeeding.
- Edits —
SEARCH/REPLACEblocks, not file rewrites. Short, and a miss is detectable, so it retries with the exact error fed back. Model-authored paths are flattened to bare filenames before they can reach the exported zip. - Versions — every generation and edit appends an immutable snapshot. Current state is just the newest one, so what you see and the history can't disagree.
Settings saved in the app win over the repo root .env, which wins over defaults. You never need to edit a file.
| Variable | Default | |
|---|---|---|
LLM_BASE_URL |
https://openrouter.ai/api/v1 |
Any OpenAI-compatible endpoint |
LLM_MODEL |
google/gemma-4-26b-a4b-it:free |
|
LLM_API_KEY |
(empty) | Free key, no card |
LLM_TIMEOUT_MS |
240000 |
One hosted call has taken 219s |
LLM_MAX_TOKENS |
(unset) | Raise if a verbose model truncates |
NETLIFY_TOKEN |
(unset) | Enables the Deploy button |
DB_PATH |
./data/unlovable.db |
Free keys, none needing a card: OpenRouter · Groq · Google AI Studio · Cerebras · NVIDIA
There is no keyless free model. Every hosted provider rejects unauthenticated completions — checked, not assumed. "Free" means free of charge, not free of signup. Local is the no-signup option.
npm test # 144 tests, no network required
npm run typecheck
npm run build:check # builds to .next-verify so it can't disturb a running dev serverEverything in src/lib/ is pure and tested. API routes are thin wiring. That boundary is deliberate — it's where the confidence comes from.
Verified by running it, not by reading the code:
- Plan → pages → preview, on a hosted model and on a local one with no network
- Chat edit landing on the right file, with the change present in the exported zip
- Version history and rollback
- Reopening a previously built project with its files, versions and model
- Export — and
grepacross the extracted zip finds nohttp://orhttps://anywhere docker compose up --buildfrom a cold daemon, reaching a model on the host- Saving a provider you have no key for is refused rather than silently accepted
Not verified:
- The Deploy button. The code path is written but has never run — there was no Netlify token to test with.
- Models other than the ones in the table above.
MIT.

