Turn a Google Sheet into a database, without trusting a robot's guesses
Every business has a spreadsheet that stopped being a spreadsheet a while ago. Forty columns, three people editing it, a tab called "OLD DO NOT USE". It wants to be a database. The only question is how much of your data survives the trip.
The tools that convert spreadsheets into apps mostly share one design choice: they guess. They read your columns, decide what type everything is, build the thing, and show you the result. It's a great demo. The problem is what a wrong guess looks like afterward: nothing. The import reports success, the row count matches, and the damage is quietly distributed through your data where you'll find it one record at a time, for months.
The date that ruins your quarter
My favorite example, because it bit a customer migration I did by hand: a
column full of dates like 03/04/2026. That's the 3rd of April in
Britain and March 4th in America, and a CSV export doesn't say which. A guessing
importer picks one, gets most rows "right" by coincidence, and now a third of
your follow-up dates are wrong in a way that looks exactly like being right.
There's no error to catch, because nothing errored.
Dropdowns have the same failure shape. If a Status column holds Active, On hold, and Closed, it should become a dropdown with those three choices. But a tool that silently invents the option list will happily add "Actve" as a fourth status because row 214 had a typo, and every view that groups by status inherits the mess.
What evidence-first looks like
When I built the Google Sheets importer for Bionic Forms, the rule was: a type is only chosen when every value in the column supports it, and where a wrong guess would be silent, the importer asks instead of deciding. The flow:
- Paste the sheet's link. If it's shared "anyone with the link", that's all you need. No upload, no export, no connecting your Google account.
- It types every column from the whole column. A field becomes an email only if every value is an email address, a dropdown only if a small set of values repeats; anything less certain stays text, which loses nothing. Rename or retype a field afterwards in the app's settings.
- Answer the questions it refused to guess. Ambiguous dates get a day-first or month-first question. That's the whole interruption.
- Import, in one step, with a meter. Columns become typed fields, rows become records, and the collection gets a table view. If any value can't be read, nothing imports — you'll never get a half-loaded collection where you can't tell which rows made it.
All or nothing sounds harsh until you've re-run a partial import and doubled four hundred records. A migration you can trust is one that either happened or didn't.
Your spreadsheet is not one table
The sheet I test against is a buyer's guide with nine tabs. One is an overview table. Seven are single products written sideways — the product name across the top, fifty attributes running down the side, the shape people reach for when a record has too many fields to sit comfortably in a row. One is a calculator.
An importer that only understands "columns are fields, rows are records" gets one of those nine and calls it a day. That is the wrong answer, because the other tabs are where the relationships live — they are the things that make things related to one another.
So the importer reads the whole book. It works out which tabs describe the same kind of record and merges them into one collection: thirty generators across seven brand tabs, one collection, and the sheet's own headings become the sections of the form. Four judgements it makes on the way, every one of them made before anything is written:
- A calculator is not data. The tab holding running totals
and a
#DIV/0!is left alone. Importing a spreadsheet's arithmetic error into a database is worse than not importing it. - A summary line is not a record. "Average" at the foot of a tab does not become a product.
- Tabs disagree, and the merge has to hold both. One tab
writes
1056under "Capacity (Wh)"; another writes2048Wh. One writes28under "Weight (lbs)"; another writes61.9 lbs. The type is decided by every tab that will share the field, not by the first one read — so where a column is a number on one tab and prose on another, it becomes text, because that is the only shape that holds both. - A unit that contradicts its column is refused, not converted.
2048Whunder "Capacity (Wh)" is read as 2048.61.9 kgunder "Weight (lbs)" stops the import, because storing 61.9 where you meant 136 is the kind of wrong you never see again.
And a refusal leaves your app exactly as it was — not half a collection with the fields added and the records missing. Unchanged.
Why a database instead of the sheet
The sheet was fine until it had to be three things at once: the place data lives, the form people fill in, and the report your boss reads. A database app splits those honestly. Fields have types, so a phone number can't wander into the date column. Records have history, so you can see who changed what. Views are separate from data, so reorganizing a screen doesn't reorganize the truth. And a public form can feed new records in from outside without handing anyone the whole spreadsheet.
Bionic Forms is early (I publish the numbers, as of 12 September 2026: nine signups who are not us, one of whom imported their own records, nobody who has typed one by hand, and nobody paying), and the paste-a-link import shipped in August 2026. It works today in the browser, and everything it builds syncs to the desktop app, which keeps working offline. A sheet that isn't shared imports too, once you connect your Google account in Settings: it is read with that account's own permission, one import at a time, with no ongoing sync. Google hasn't verified the app yet, so its consent screen shows a warning first — the paste-a-link path needs no permission at all.
Or skip the assembly entirely: tell us what your current system does and we will build a working version and show you. No fee and no card — what we build fits the free plan.