Skip to main content
ASoc
Comparison

Nuxt vs Bubble: Which Half of the Work Do You Want to Own?

Bubble includes the expensive half: auth, data model, workflows. Priced in this repo's own files — 8 migrations, 6 RLS policies, 31 test files — and what code buys back.

The ASoc Team9 min read

Bubble is a hosted no-code platform: you draw the interface, define the data, wire the workflows, and Bubble runs all of it. Nuxt is a Vue framework you build and deploy yourself. The comparison is not design quality or page speed — it is which half of the work you want to own, because Bubble includes the half that costs the most to write.

Most code-versus-no-code posts argue about speed and "real" development. That argument is noise. This one is written from the code side by a storefront that had to build, by hand, exactly the subsystem Bubble ships in the box — auth, a data model, entitlements, checkout, gated downloads — and can therefore price it. We do not run Nuxt here (this site is Next.js), so treat every Nuxt statement as general and every measurement as ours. For the same decision against a site builder rather than an app builder, read Next.js vs Wix; for the Vue-side framework question, Next.js vs Nuxt.

The comparison that matters

AxisBubbleNuxt (or any code framework)
Who builds a screenAnyone, in a visual editorA developer, via a commit and a deploy
Database and schemaIncluded, defined in the editorYours to choose, model and migrate
Auth, sessions, password resetIncludedYours, with no default behaviour at all
Workflows and business logicVisual workflow editorCode you write and test
PaymentsPluginAn integration plus a signed webhook you verify
Hosting, scaling, uptimeBubble's problemYours to configure
Cost shapeA subscription, with usage metered by planEngineering time first, then near-zero hosting
The artifactAn app inside the platformA repository that is the site
Output per product/recordPlatform-rendered pagesAny URL shape you can generate
Response headers, CSP, cachingPlatform-controlledYours to set, and yours to get wrong

Bubble's plan tiers, usage metering and plugin costs change; read their current pricing page rather than a blog post's snapshot of it.

Where Bubble genuinely wins, priced in our own files

The honest version of this comparison starts with what the no-code side includes. Commerce on this storefront — the part a Bubble template would have given us on day one — is this much code and schema:

What it doesWhere it livesSize
Schema, row-level security, atomic RPCssupabase/migrations/8 migrations, 6 CREATE POLICY statements
Who owns what (pure, no I/O)src/lib/entitlements.ts75 lines
Authorizing one downloadsrc/lib/download.ts373 lines
Issuing the signed URLsrc/app/api/download/route.tsA Node route handler
Verifying the payment webhooksrc/app/api/webhooks/lemonsqueezy/route.ts69 lines
Proving it all still workssrc/**/*.test.ts31 test files

None of that is visible to a buyer. It renders nothing. It exists so that a paid customer can download a zip and nobody else can — the single feature Bubble would have handed us with a plugin and a database table.

And the cost is not only the first write. Here is one comment from the download route, which is the kind of thing you own forever once you choose code:

// src/app/api/download/route.ts
async listActiveSlots(userId) {
  // The admin client bypasses RLS — this explicit `user_id`/`status`
  // filter IS the security boundary (R-5), not defense-in-depth on top
  // of RLS.
  const { data, error } = await admin
    .from("entitlement_slots")
    .select("kind, product_slug, framework, status")
    .eq("user_id", userId)
    .eq("status", "active");

Deleting the .eq("user_id", userId) on that query compiles, passes a smoke test, and hands every user everyone else's entitlements. A visual platform has no equivalent foot-gun there because it has no service-role client to misuse. If your project's value is in its workflows rather than its pages, that is a real argument for Bubble, and engineers systematically undercount it.

Where the code side wins: the output is a URL, and you own what it says

Bubble renders your app. Code renders your HTML. For a product people search for, that difference lands in a few specific places.

This repository has 27 page.tsx files and prerenders the whole storefront: 111 products, 7 category hubs, 377 blog posts and the static pages, all from typed data rather than hand-authored screens. The machinery that decides what search engines are told about those URLs is ordinary code, which means it can carry rules a platform's SEO panel has no field for.

One of them is a rule this repo learned the hard way, and it is three lines of ordinary logic rather than a setting. sitemap.ts used to stamp the build clock on every URL as lastModified. Telling Google that 100+ pages changed today, at every deploy, is a claim it can check and find false — and the field it stops trusting is the one that schedules crawls. Each product now carries the newer of its latest changelog entry and the date the storefront's rendering last changed; each hub carries the newest date among the products it lists. A test asserts every sitemap date is midnight UTC, because a clock reading never is, and that assertion is what found the static routes still doing it. The longer version of that story is in Next.js vs Wix.

The second one is a consistency rule that only exists because the sitemap is code: a product marked coming-soon is noindex and absent from the sitemap, derived from one field. Asking Google to index a URL while telling it not to is a contradiction you can express in about four clicks in any CMS, and cannot express here at all.

Neither of these is a complaint about Bubble specifically. It is the general shape of the trade: a platform gives you the fields it decided to give you, and a repository gives you the whole surface — including the parts you can get wrong, as we did.

The artifact question, which is different for a product company

If the thing you sell is code, the platform question answers itself. This storefront's product is a zip of source files; the entitlement that unlocks it is a catalog slug, and that slug is a stable identity we are not allowed to delete once it has sold:

Never delete a slug that has ever sold — it is a stable entitlement ID, and removing it breaks owners' dashboards and downloads.

That is a constraint we can only honour because the catalog, the entitlement engine and the download route are all in one repository that we version. If the same business ran inside an app platform, the identity would live in the platform's database, and "export" would mean a CSV, not the running system.

For a project whose output is an internal tool, a marketplace MVP or a booking app, none of that applies, and Bubble's included half is worth more than the repository would be.

Choosing, in four questions

QuestionIf yes
Is the hard part workflows and a data model, not pages?Bubble. It includes the expensive half
Does a non-developer need to change things daily?Bubble, or a code site with a CMS behind it
Do you need an indexable URL per record, with full control of its head tags, sitemap entry and headers?Code — Nuxt, Next.js or Astro
Is the product itself source code, or does it need to leave the platform intact?Code. The repository is the asset

If two of those pull in opposite directions, the usual resolution is a split: a marketing front end as static pages, and the app behind an /app subdomain on whatever runs it best. That is the architecture this storefront uses — a fully static storefront with the commerce routes as the only dynamic surface.

Troubleshooting the decision

SymptomWhat is actually happeningWhat to do
"No-code is cheaper" turns out false at month 9Usage metering and per-seat plugin costs scale with successPrice your expected usage on the current pricing page before committing
"Code is cheaper" turns out false at week 3You are building auth, sessions and billing from scratchUse a managed backend; the 8 migrations above exist on top of one
Pages don't rank on either platformThin content and no internal links; not a platform propertyFix the content model first, the stack second
A rebuild is proposed to "get SEO back"Rankings follow content and links, not the rendererRead framework migration without losing rankings before moving anything
Platform pages look fast but Search Console disagreesField data, not lab data, and an app runtime has a floorCompare like with like: static HTML has no runtime to boot

Frequently asked questions

Is Nuxt better than Bubble? For a public, content-heavy site where every record should be its own indexable URL, yes. For an internal tool or an app MVP whose value is in workflows, no — Bubble removes the most expensive work, and Nuxt gives you none of it for free.

Can I move a Bubble app to Nuxt later? Treat it as a rebuild rather than a migration: the interface, workflows and data model are expressed in the platform's own terms. Export your data early and often, and keep anything that must outlive the platform — content, pricing, product identity — in a form you can read without it.

Do I lose SEO by building in a no-code platform? Not automatically; the platform's rendering and the fields it exposes set your ceiling. The difference is control, which cuts both ways — this repo's own sitemap shipped a measurably false lastModified for months before a test caught it.

What does the code route actually cost? On this storefront: 27 route files, 8 migrations, 31 test files, and a CI pipeline that runs lint, Prettier, the test suite and a production build on every push. The hosting cost of the static half rounds to nothing; the pipeline is the price.

If the site is the product, start from a finished one

For a marketing front end, the fastest honest answer is neither a platform nor a blank framework. ASoc Mind, ASoc Momentum and ASoc Neuron are landing sets whose source you own outright — tokens, dark mode and SEO surfaces already wired.

Browse the sets: Next.js landing page templates and Tailwind landing page templates.

Keep reading

Comparison9 min read

Nuxt vs Bulma: "No JavaScript Included" Is the Whole Comparison

Bulma 1.0.4 ships 96 files and zero .js, so your Vue code owns every interaction. The six components this storefront had to write for exactly that reason.

Read more
Comparison7 min read

One-Time Payment vs Subscription: What the Webhook Handler Has to Know

Our entitlement engine is 75 lines with two status states; our LemonSqueezy webhook recognizes two event names. That's not a shortcut — it's the real cost gap between the two pricing models.

Read more
Comparison8 min read

Playwright vs Vitest: Why This Repo Runs Both for Unrelated Jobs

Vitest is a committed dependency gating 305 tests on every push. Playwright is deliberately not a dependency at all — installed ad hoc for a script that runs a few times a year.

Read more