How ContentForger Turns a Plain-English Brief Into a Deployable App
The generate flow, step by step, as the product actually runs it
By Thomas Lovas Lokoy · 2026-10-16

What you will build
- • A clear mental model of what happens between pressing Generate and receiving files
- • Your first generation, exported as a ZIP with its folder structure intact
- • A follow-up prompt that refines the same app inside one conversation
- • A short list of what the output includes and what it deliberately leaves to you
Prerequisites
- • A ContentForger account (the Free plan is enough; no card is needed to sign up)
- • A modern browser
- • Node.js and npm installed, only if you want to run the output locally at the end
Chapter 1: What You Are Actually Sending
~7 min
The Generate page at /generate looks like a text box with a button. Under it, every click sends one request to a single endpoint, and the shape of that request decides most of what comes back. It is worth knowing the shape.
You must be signed in. A signed-out visitor who opens /generate is redirected to the sign-in page and brought back afterwards. The anonymous demo at /demo is a different, smaller thing: it takes a prompt of 10 to 500 characters, runs on Claude Haiku with a short output budget, allows three runs an hour per network address and does not save anything.
The main form has three inputs on a first turn.
- The prompt. Free text, between 1 and 4,000 characters. Anything longer is rejected before any model runs.
- A template, optional. There are fifteen, from SaaS Starter and AI Chat App to Invoice Generator and Community Forum. Picking one does not swap in prewritten code. It prepends that template's own prompt to yours, with your text appended as "Additional instructions". You can read the exact template prompt on every /templates page before you choose it.
- A style, one of minimal, playful, dense, brutalist or editorial. It becomes a single line in the instructions the model receives: "Design style: <your choice>". Minimal is the default.
Ctrl+Enter (Cmd+Enter on a Mac) sends the form. There is a Stop button while a generation is streaming; it cancels the connection from your side.
The first time you open the page you will see a tip card with three habits that make prompts work: name your stack, list your pages, state your business rules. That card is dismissable and does not come back once dismissed. The habits are worth keeping. The generator fills gaps with sensible defaults, but every default it picks is one you did not choose.
Chapter 2: The Gates Your Request Passes Before Any Code Is Written
~8 min
Nothing is sent to the model until your request has cleared a short sequence of checks. Each one exists to protect either you or the service, and each one produces a different message when it fails. Knowing which message means what saves a lot of confusion.
Gate one is a per-address rate limit. The generate endpoint allows twenty requests an hour from one network address, on a sliding window. That window is shared with the AI Project Brief tool. If you hit it you get a plain "Rate limit exceeded" response. This limit is independent of your plan.
Gate two is sign-in. No session, no generation.
Gate three is input validation. Prompt length, the optional template name, the optional style, and, for a change request, the project it builds on are all checked. A malformed request is answered with "Invalid input".
Gate four is a content-policy check. It is a small, explicit block list aimed at a handful of harmful categories such as phishing kits, malware and credential theft. It is not a general-purpose moderation model. A blocked prompt returns "This request was blocked by our content policy" and nothing is spent.
Gate five is your daily allowance. The service counts the generations you have started since midnight UTC and compares the count with your plan's limit: 5 on Free, 100 on Pro, 1,000 on Agency. Two details matter here. The count is taken from your history rows, and a row is written the moment a generation starts, so a run you stop early, or one that fails, still counts. And the clock is calendar-day UTC, not a rolling 24 hours. When you are at the limit the response is a 429 with the message "Daily generation limit reached".
Gate six is a site-wide monthly spend ceiling on model usage across all users. When that ceiling is reached, every generation on the service answers 503 "Service temporarily at capacity" until the month rolls over. The ceiling resets on the first of the month, UTC.
There is one more response worth recognising. If the service cannot read your usage at all, for example because the database did not answer, it refuses rather than guess, and tells you so: "We're temporarily unable to verify your usage, please retry", with a sixty-second retry hint. That is a 503, not a 429. It means try again in a minute, not come back tomorrow. The guards are deliberately built to fail closed, because an unmetered model bill is a worse failure than a short delay.
Chapter 3: What the Model Is Told to Produce
~8 min
Once your request is through the gates, it is combined with a long system instruction that you never see on screen. That instruction is the real reason ContentForger output looks the way it does, so this chapter summarises it.
The opening line sets the role: an expert full-stack engineer producing production-ready Next.js 16 App Router code in TypeScript with Tailwind CSS v4. Your style word is added as a single sentence.
Then comes the output format, and this part is strict. Every file must begin with a marker line of the form ---FILE: path/to/file.tsx--- followed by a fenced code block whose language tag matches the file extension. No commentary is allowed between files. The model is also told to emit files most-interesting-first: the page you asked for, then the components it imports, then layout, global styles and config last. That ordering is why the first file you see streaming is usually app/page.tsx rather than a font import.
Then a list of requirements the generator is told to meet on every run. The notable ones:
- TypeScript strict mode with no any types, and await params and await searchParams in every dynamic route.
- next/image for images, generateMetadata on every route, and a robots.ts, not-found.tsx and error.tsx.
- A dynamic app/sitemap.ts that enumerates real records rather than a hard-coded list.
- A .env.example listing every environment variable the app needs, and a README with setup and deploy instructions for Cloudflare Pages and Netlify.
- When the app stores data: a real database layer (Supabase/Postgres) with a supabase/schema.sql including Row Level Security, Zod schemas as the single source of truth for types, a typed data-access layer, Route Handlers that validate input and return proper status codes, and Server Actions for mutations.
- When records belong to users: Supabase Auth, with ownership enforced both by RLS and by a server-side check.
- Runnable tests: Vitest unit tests for the schemas and data layer plus one Playwright end-to-end test, with test scripts in package.json.
- A short forbidden list, including react-icons, moment.js, inline styles and a specific dark-navy background colour.
Finally, a plan-specific tail. Free generations are asked to include a "Built with ContentForger" comment in the footer; that comment is the watermark the pricing page refers to. Pro generations receive deploy instructions for Cloudflare Pages and Netlify instead. Agency generations receive white-label instructions built from your saved brand name, logo URL and primary colour, with an explicit instruction not to mention ContentForger or NorwegianSpark anywhere in the code.
One honest caveat about all of the above. These are instructions to a language model, not a compiler's guarantees. A given output may miss an item, especially when the output budget is tight. Chapter 6 and the pre-launch checklist in the journal explain what to verify.
Chapter 4: Watching the Stream and Reading the Output
~6 min
The response is a live stream. The server forwards the model's text as it is produced, so the right-hand panel starts filling within a few seconds and keeps filling until the model stops. While it runs you see a character count and an elapsed-seconds timer under the prompt.
The viewer does something useful with that raw text. It scans for the ---FILE: markers described in the previous chapter and splits the stream into separate files, each with its path and language. Those files appear as a tree down the left of the panel, with icons by type, and the first file is selected automatically. If the output contains no markers at all, which happens when the model answers in prose instead of code, the viewer falls back to showing the raw text, and the download button gives you a .txt file rather than a ZIP.
The toolbar above the tree has, in order: an Edit toggle, Copy this file, Copy all files, Download ZIP, a Deploy guide, and Share. Edit mode turns the active file into a text area so you can make a quick fix in the browser. Those edits live only in the page: Copy all includes them, but the ZIP is built from the original generated output, so if you have edited in the viewer, use Copy all or re-apply the edit after download.
When the stream finishes a stats line appears under the panel: the model that ran, input tokens and output tokens. The model is decided by your plan, never by the prompt: Claude Sonnet 5.5 for the first app of the day on Free and Claude Haiku 5.5 after that, Claude Sonnet 5.5 on Pro, Claude Opus 5.5 on Agency.
Since October 2026 there is no fixed output cap per app. The app is planned first and then written six files at a time, and each file is saved the moment it is finished, so a closed tab or a dropped connection loses at most the file in progress; reopen the same build link and it carries on. The next chapter covers follow-ups.
The Stop button aborts your connection. The server-side record of the attempt still exists and still counts against your daily allowance, as noted in Chapter 2.
Chapter 5: Change Requests: Refining One Project
~5 min
After the first build finishes the form changes. The template and style selectors disappear, the prompt box becomes a Change request box, and up to three suggested next changes appear as buttons. You are now working on a project.
A change request is a new build linked to that project. The planner reads the project's current files together with your request and lists only the files to add, change or delete. Those files are written and saved one by one, like a first build, and every other file is carried over. The template and style are only applied to the first build.
Four practical consequences follow.
- Every change request counts against your daily change allowance, which is separate from new apps: 10 a day on Free, 300 on Pro, 3,000 on Agency.
- The project keeps the model that built it.
- A change request only starts once the current build has finished; if a build stopped part-way, reopen its link so it completes first.
- The ZIP always contains the whole project, so there is nothing to merge by hand inside ContentForger. Edits you made on your own machine are not part of the project, though; paste a hand-edited file into the request if the change must build on it.
The New project link clears the project and brings back the template and style controls. Start a new project when the direction changes substantially; use change requests when you are refining the same app.
A good rhythm for a first project: one first prompt that names the pages, the data and the stack; then two or three change requests, each asking for one concrete change.
Chapter 6: What You Get, and What You Do Not
~6 min
At the end of a generation you have a set of source files. This chapter is the honest inventory of what that means.
What you get:
- A ZIP named after your app's title (for example minimal-coffee-shop-landing.zip) containing every file at its path, so app/page.tsx lands in an app folder, lib files in lib, and so on. Unzip it and you have a project directory.
- A record in your dashboard. Every generation you start is saved to your history at /dashboard with its prompt, model, token counts, status and the full output, twenty per page, newest first. You can reopen any of them and copy the output again later. This is true on every plan.
- A share link, if you want one. The Share button creates a public page at /share/<id> showing the prompt, the model and the files, with its own copy and ZIP buttons. Anyone with the link can view it. There is currently no control to un-share a generation, so treat that link as public once created.
- Usage stats per run: model, input tokens, output tokens.
What you do not get:
- Hosting. ContentForger does not run your app anywhere. The deploy guide in the viewer gives you the commands; you run them against your own Cloudflare or Netlify account.
- A database or any credentials. The generated .env.example tells you which keys the app expects; you create those accounts and paste the values yourself.
- Installed dependencies. The ZIP contains source files, not a node_modules folder. You run npm install.
- A guarantee that it compiles. The generator is instructed to produce strict TypeScript that builds, and usually does, but the proof is running npm run build on your machine.
- A repository. Nothing is pushed anywhere. Putting the project under version control is your first move after unzipping.
If that list reads as a lot left to you, the next tutorial, From Generated Code to a Live Site, walks through exactly those steps in order, using the same commands the in-product deploy guide shows.