All posts
build logreacttypescript

How I Built Thunder Forms, a Free Form Builder

The two-year journey of building Thunder Forms, a free form builder with Google Drive uploads, server-side quiz grading, AI drafting, and the walls I chose to keep.

By

Thunder Forms is a free form builder at thunderforms.in. Respondents fill public links without creating an account, file uploads land directly in the form owner's Google Drive, and none of it sits behind a paid plan. AI can draft your form from a single sentence, and the template library already has event registrations, quizzes, and feedback forms ready to copy into your dashboard and edit.

That's the summary. The rest of this post is the journey: what I built first, which decisions took the longest to get right, and the features I deliberately kept out.

Why it exists

The first commit is dated Oct 31, 2024. I was collecting documents and registrations for college events, and every tool in the category drew the same two walls. File uploads sat behind a paid tier, and respondents were pushed into an account before they could type a single answer. For an event form, both are conversion killers: guests churn at the login screen, and the organizer either upgrades or gives up on accepting files.

So the product definition wrote itself before the architecture did. A free form builder where those two walls are exactly the parts that stay free. Respondents never sign up. Owners never pay to receive files. Everything else, templates, themes, exports, decorates that promise rather than substitutes for it.

Getting the shape right

The structural decision that everything else still leans on came early: every form is a single tree of pages, sections, and fields, stored as one JSON document. The contract lives in one file, form-structure.ts, and the builder edits it, the respondent renderer reads it, the validators check it, the template library instantiates it, and the AI generator produces it directly into the same shape.

I talked myself into separate structures for builder state and published form more than once. Every draft needed a converter, and every converter became a place where the two sides drifted apart. Collapsing to one canonical tree deleted that entire class of bug. When quiz settings, themes, and expiry rules arrived later, they became properties on the same document instead of side tables, and the Google Forms importer could convert a rival's form straight into a shape the builder already understood.

The upload problem

Uploads are where the free promise gets interesting. The goal was to accept files on public links and store them somewhere the owner controls without asking anyone for much. Google Drive was the answer: connect once, and the app delivers files on the owner's behalf.

The engineering behind that sentence is the most careful part of the codebase. The owner's Google connection lives on the server, encrypted at rest, and the browser never holds a refresh token. Each upload field can choose its own Drive folder destination, so one field can feed a shared team folder while another files into a private archive. Limits are stated plainly: 12 MiB per file, ten files per field, visible in the docs with no surprises at upload time.

Watching a guest attach their submission from a public link, with no account and no payment screen in sight, never got old. It is the one part of the product I stopped worrying about early.

Growing into a product

Once the skeleton held, the serious features arrived through the same door.

Quizzes came first among them. A quiz form carries answer keys with per-question points, and grading runs on the server where the full stored tree lives. Before any respondent payload ships, one function, stripQuizAnswerKeys, removes the answers so nothing sensitive reaches the respondent's browser. Scores release immediately or after manual review, and the result email lands on whichever field the owner marked for it.

AI drafting came next. The generation route gives the model narrow discovery tools instead of an open prompt, field defaults come from the registry the builder already trusts, and a structural validator refuses anything that does not arrive as canonical pages, sections, and fields. Bad generations fail loudly with a 502 and never reach the canvas. Templates hand validated structures over through the same envelope, so an AI draft and a library template are indistinguishable once they land.

Google Forms import, Sheets sync, CSV export, and expiry by date or response count filled in the rest of the daily-driver checklist. Along the way, an early version placed 1st Runner Up at Appocalypse 2.0 MAIT, the first outside signal that the bones were right.

The walls I kept

A journey post without the avoided features would just be a pitch, so here are the ones still standing.

Conditional logic is the biggest outstanding ask, and the reason it is absent is structural: it turns the tree into a graph, and the renderer, the submit API, the importer, and analytics all branch at once. It will ship when it can ship complete. Analytics reports views, visitors, devices, and referrers and stops there, with the four numbers labeled plainly and the CSV export carrying everything else. Custom domains are the infrastructure line I am not willing to maintain at free-tier prices.

Each of these is written on the product pages themselves. Trust survives documented gaps far better than it survives a feature matrix that oversells.

Where it stands

Thunder Forms is live and free at thunderforms.in. The templates are the fastest way in: pick one, edit it in the builder, share the public link, and watch files arrive in your Drive. If you spot a hole in any of the decisions above, the contact page is open.

Two years in, the tedious parts are built and the interesting parts remain. If you need a form builder that asks nobody to pay or log in, it is one link away.