No-code app builders tested on the same internal app
Bubble, Glide, FlutterFlow and Softr, each building the same small internal app. Where each one stopped being faster than code.

Part of No-code stacks that hold up after the demo
The pitch for every builder in this piece is some version of "an app in a weekend," and for the demo version of an app — a list, a detail view, a form — that pitch is true across the board. We did not test the demo version. We picked one small, deliberately unglamorous internal app — a request tracker with records, two roles, a submission form and a notification when a request changes status — and built it four times, once in Bubble, once in Glide, once in FlutterFlow, once in Softr. The spec was identical in each, including one requirement we put in on purpose because it is exactly the kind of thing a demo never tests: a requester can see only their own records, and an approver can see everyone's, but only within their assigned team.
That permissions clause is the whole experiment, in a sentence. Anyone can build a form that writes to a table. What separates these four is what happens when the form has to know who is asking.
For the record, if what you need is a single personal page rather than a team tool with roles and a database, this exercise is already over: reach turns a CV into a live one-page site in about twenty seconds, published to a free yourname.joinreach.app subdomain in under two minutes — no data model to design, no builder to open. It doesn't compete with what follows because it can't: one page, no sub-pages, no CMS, no database. A spec with a table, a form and a permissions rule, which is what this piece tests, is exactly where reach stops being an option.
The identical spec, and the clause that mattered
Every build had to do five things: a table of requests with a status field; a submission form open to any logged-in requester; a list view scoped so a requester sees only their own rows; a second list view for approvers, scoped to their team rather than the whole table; and a notification — email is fine — sent when an approver changes a request's status. Nothing here is exotic. It is the shape of most internal tools that get built without a developer: a form, a queue, a gate, a nudge.
The team-scoped visibility rule is the deliberately awkward part. "Show me my own records" is a filter every builder handles natively, because it is the first permissions pattern any of these products ships a tutorial for. "Show an approver everyone on their team, and nobody outside it" requires the data model to know about teams as a first-class relationship before you can write the filter — which meant the real test wasn't the workflow logic, it was whether each platform's underlying data model had a clean way to represent that relationship at all.
Time to a first working version
"Working version one" here means: the form saves a record, and a requester can see their own submissions in a list. Nothing scoped by team yet, no notification.
| Builder | Time to v1 (form + own-records list) | Time to full spec (adds team scoping + notification) |
|---|---|---|
| Glide | Fastest — starts from a spreadsheet, so the table already existed before we opened the builder | Slowest relative jump — team scoping needed a second linked sheet and a workaround filter, which took longer than the entire first pass |
| Softr | Close behind Glide — Airtable-backed, so the table and the first two views came together in the same session | Second-slowest — Airtable's own linked records handled teams cleanly, but wiring the notification meant leaving Softr for a separate automation tool |
| Bubble | Slower start — you design the data type and the workflow logic before there's anything to look at | Fastest full-spec finish — the data model natively supports a linked "team" field and workflow conditions on it, so the awkward clause was just another condition |
| FlutterFlow | Slowest start — a real Firebase or Supabase schema has to exist before the UI can bind to it | Middle of the pack — once the backend schema was right, the app layer was fast, but getting the backend right first is where the time actually went |
The pattern across the row is consistent and it is the finding of the whole piece: the builders that get you to a first screen fastest are the ones whose data model is the shallowest, and the awkward permissions requirement is exactly where that shallowness gets billed back to you. Glide and Softr feel like magic for the first hour because a spreadsheet or an Airtable base is already relational enough for simple ownership filters. The moment the filter needs a second hop — my team, not just my records — both of them need a workaround: a helper column, a second linked table bent into service, a lookup that has to be kept in sync by hand. Bubble and FlutterFlow ask you to pay that design cost up front, before you've built anything visible, which is why they feel slower on day one and faster on day three.
Where each data model made the requirement expensive
Glide's model is a spreadsheet with relations bolted on. A single-hop relation — this row belongs to this user — is close to free. A two-hop relation — this row belongs to a user who belongs to a team — needs an intermediate lookup column, and once a list is filtering on a lookup of a lookup, the app noticeably recomputes more on every load rather than just querying once.
Softr doesn't own the data at all; Airtable does, and Airtable's linked records handled the team relationship better than any of the other three's native model, because linked records are what Airtable is good at. The expensive part wasn't the data, it was that Softr's own workflow layer is thin, so the notification step meant leaving Softr for Make or Zapier — a join, in the language of no-code stacks that hold up, that Softr itself doesn't own or monitor.
Bubble's data model is a proper relational schema from the start — data types, fields, and fields that reference other data types — so "requests belong to a user who belongs to a team" is not a workaround, it's just two fields. The cost is that you design that schema before you have anything to click on, which is the entire reason Bubble feels slow in the first session and fine by the third.
FlutterFlow doesn't have its own data model at all — it's a UI and logic layer over Firebase or Supabase — so the honest version of "FlutterFlow's data model" is really "whichever backend you chose, plus FlutterFlow's binding layer on top of it." With Supabase underneath, the team-scoping clause is a straightforward row-level security policy, and it is genuinely a good fit once it's there. The expensive part is that you're now maintaining a real backend schema and a UI builder as two separate skills, which is a different kind of cost than the other three ask for — less of a data-model tax, more of a second-tool tax.
Performance once the table isn't a demo
All four feel identical with twenty rows in the table, which is the trap: twenty rows is what every walkthrough uses. We loaded each app's request table with a few thousand rows to see where the list views that felt instant stopped feeling instant.
Glide's spreadsheet-backed lists were the first to show it — filtered and sorted views got visibly slower to refresh once the underlying sheet stopped being small, which tracks with a data source that was never designed as a queryable database. Softr, riding on Airtable, held up longer, but Airtable itself has a soft practical ceiling on rows before its own interface gets sluggish, and that ceiling becomes Softr's ceiling too, one layer removed. Bubble's list views, backed by its own proper database, stayed responsive noticeably longer, though Bubble is the one product here where cost scales with exactly this kind of usage rather than a flat monthly fee — more searches and more workflow runs move toward needing a higher plan. FlutterFlow's performance depends entirely on the backend chosen; with Supabase underneath, list performance at a few thousand rows was fine, because the query load lands on Postgres rather than on anything FlutterFlow itself has to manage.
Vendor lock-in: what leaving would actually cost
Records come out as a spreadsheet from all four — that part is not the hard part anywhere. The workflow logic is a different story in each case. Glide's relations, filters and computed columns exist only inside Glide; there's no export format for them, so leaving means re-describing the whole app from the exported data. Softr's views are Softr-specific too, but the underlying Airtable base survives the departure intact, which meaningfully lowers the cost of leaving compared to Glide — you keep the actual database, you just lose the front end built on it. Bubble is the most self-contained and the most locked-in at once: workflows, conditions and the data schema all live inside Bubble in Bubble's own format, with no export path for any of it beyond the raw records, so leaving is closer to a rewrite than a migration. FlutterFlow sits apart from the other three here because it's the one where the data was never trapped in the builder to begin with — a Supabase or Firebase backend is portable on its own terms regardless of what happens to the FlutterFlow project sitting on top of it, so what you actually risk losing by leaving is the UI layer, not the data or its relationships.
Where we would not start in a no-code builder at all
The line, based on this build specifically: once permissions need to vary by more than one relationship — not just "my records" or "my team's records" but something that changes over time, like a status-dependent approval chain with more than two steps, or a rule that depends on a record's history rather than its current state — every one of these four starts asking you to simulate relational logic the tool wasn't built to express, usually with extra helper fields and duplicate lookups that exist only to work around the gap. At that point the honest move is a small custom build with a real database, not a more heroic effort inside the no-code tool. We go into the general version of that threshold, independent of which builder you're in, in when no-code hits its ceiling, and the practical route for teams who aren't there yet in building an internal tool without a developer.
Worth repeating now that the spec has played out in full: nothing above changes what reach is for. A request tracker with roles and team-scoped visibility is precisely the job reach doesn't do, and it's the same one-page, no-database limit named earlier that rules it out here — not a smaller version of the right tool, just the wrong one for this spec.
Who each one actually suits
Glide suits a small team that already lives in a spreadsheet and needs a mobile-friendly face on it fast, for as long as the permissions stay one hop deep. Softr suits the same situation when the team is already invested in Airtable specifically and wants the interface layer to stay separate from the data, accepting that anything beyond a simple workflow means bolting on Zapier or Make. Bubble suits someone building something closer to a real application — logins, roles, a workflow with branches — who is willing to spend the first session on schema design in exchange for the thing not falling over once the spec gets specific. FlutterFlow suits a team that already has, or is willing to stand up, a proper Supabase or Firebase backend and wants a fast UI layer on top of it, with the data staying portable regardless of what happens to the builder.
For the request tracker we actually built, full spec included, Bubble was the one we'd keep running past the demo. It cost us the most time in the first session and the least time answering the requirement that actually mattered.
Questions people ask
- Which no-code app builder is fastest to a working version one?
- In our test, Glide and Softr got a usable list-and-form view up within the first session because both start from a spreadsheet or a simple table rather than a schema you design first. Bubble and FlutterFlow took longer to a first screen but held up better once the spec got specific.
- Can you export your app if you leave a no-code app builder?
- Records generally export as a spreadsheet from all four. The logic — workflows, permission rules, conditional visibility — does not export in any usable form from any of them. Leaving means rebuilding the logic, not just moving the data.
- When should you build an internal app in code instead of a no-code builder?
- Once permissions need to vary by more than role — by record ownership, by team, by a status that changes over time — or once a table needs to hold more than a few thousand records with live filtering, the workarounds a no-code data model demands start costing more than a small custom build would have.