Migrating your Bubble app to Next.js/Supabase and how it can go wrong

Hey everyone :waving_hand:

We’ve had a few clients recently come to us @ Not Quite Unicorns partway through a Bubble to Next.js/Supabase migration. They sometimes have 100k+ users and millions in funding, so these are actual complex apps.

We’re seeing a lot of the same and I wanted to drop some advice for other people trying to do the same thing.

The first part of migrating your app feels fast and refreshing because rebuilding UI and basic logic is relatively easy. You can use Claude Code, Cursor, v0, Lovable, etc. to get a decent-looking Next.js app quickly. You can create tables in Supabase quickly. But is a bit misleading, because most of the time, the new app has exactly the same technical debt that you’re migrating away from Bubble from.

Most Bubble apps don’t need to migrate to code, but the biggest reason they do, more than any Bubble limitations, is that they were badly built in the first place. A lot of founders are doing the exact same thing again, and are basically just moving their technical debt to a different stack (many of them would’ve been able to resolve their issues in Bubble and scaled without ever having a need to migrate!).

It’s sooooo frustrating to see people falling into this trap because in a few months after migrating they find the same kind of issues they faced on Bubble and learn that the problem was not with the platform but how their app was built.

AI code is useful, but it is not magic

People are using AI code tools to avoid understanding the Bubble app and their new code. That does not work.

Claude Code can generate a nice component. It can write an API route. It can create a Supabase table. It can help with RLS policies if you give it the right context (as an aside, don’t use RLS with direct client DB connections, it’s a security nightmare…).

If your migration plan is “export the Bubble JSON, give it to Claude, and ask it to rebuild the app”, you are probably going to have a very bad time.

The Bubble app JSON is useful (and Buildprint even more so), but it isn’t magic!

At best, it gives you a spec sheet of what exists at the moment. But when the AI agent sees it, it also takes it as an assumption that what is in the Bubble app source code should also be in the new codebase. It will almost never suggest doing things a better way / different way that’s now possible in code.

That’s your responsibility to decide, and you can only do that if you truly understand what you’re building and stay heavily in the development loop during migration.

Moving technical debt is not the same as fixing it

So, most migrations end up recreating the same technical problems in a new stack.

If the Bubble database is confusing, copying it table-for-table into Supabase does not suddenly make it good architecture, for example.

A migration is an opportunity to decide what the app actually is. It’s the only other time the business will have to reset its technical debt and design the new application based on what the application has become (rather than what it was envisioned to be when it was first built).

Some parts can for sure be copied fairly directly. But some parts always need to be entirely redesigned. That is annoying, but it is also the point. If you migrate without doing that thinking process, you can easily spend a ton of money building a cleaner-looking version of your app that has the same mess.

External backends with Bubble are where apps go to die

Another thing I see is teams trying to migrate table by table into Supabase/Xano/whatever while keeping Bubble as the frontend.

Sometimes this is fine for a very specific, contained use case. For example, if you have one heavy reporting table, or one isolated data process that really benefits from being outside Bubble.

But as a general full app migration strategy, Bubble + external backend is often where apps go to die.

Bubble owns some data, Supabase owns some data, some workflows run in Bubble, some logic runs in Edge Functions or API routes or Next server actions. Everything moving between the two systems has to pass through the API Connector or some plugin layer. The app now has two sources of truth, two permission models, two debugging surfaces. And you haven’t even moved off of Bubble!

Bubble is strong because it’s a full stack platform. Once you pull the database out, Bubble becomes an extremely clunky frontend on top of an external backend. Lots of apps we work with came to us after getting stuck here and it can almost kill their business because using an external backend just kills their development velocity.

How to migrate your Bubble app to code correctly

If you are planning a Bubble to Next.js/Supabase migration properly, you should generally build the new codebase in parallel than slowly turn the existing Bubble app into a hybrid Bubble/Supabase/Next.js situation. The new build will then catch up with product development on the existing Bubble app.

Build it module by module (exactly what a module is will depend on the app), and after each module, verify full data migration works up to that module. The other big mistake people keep running into is adding all the data migration functionality at the end. It’s a bit like how in Bubble if you save privacy rules til last you kind of screw yourself over and it creates all sorts of problems. If you later find your Bubble data structure cannot be transformed to fit the new database structure and you’ve built your whole new build logic on top of it, you’re kind of screwed.

For B2B apps, self-serve migration is generally best. You can let customers move over at their own pace, especially if accounts are fairly isolated from one another. It means they can move at a time that’s convenient, and it also means you don’t have one big cutover today (instead it’ll be a slow ramp up which helps fix any scale issues you didn’t anticipate).

For B2C apps, marketplaces, social apps, or anything where users interact heavily with shared data, self-serve migration obviously doesn’t make sense. You usually need a proper cutover plan after dry running the data migration and testing the new system properly.

There are exceptions to the above of course, but the main principle is that the migration needs to be done thoughtfully, not just passing your app logic to Claude Code and hoping for the best.

Final thought

Most Bubble apps don’t need to leave Bubble. The stories of ‘I migrated my Bubble app to code in two weeks!’ are mostly crap from people without apps that are running at any kind of serious scale or complexity (and the people advertising that they’ll migrate your app based on your JSON file are basically vibe coding it and doing exactly what I’m suggesting not to do).

But if you are going to migrate to Next.js/Supabase, take it slowly and do it properly (ideally with a team that knows both Bubble and code). That will give you the best chance of success.

How rare (or not) are competent next.js developers in the Bubble scene?

What exactly are the bad practices? Lots of vagueposting here.

You should basically never give AI direct access to Bubble app JSON (except possibly for UI).

Self explanatory

As above!

Any migration, Bubble or otherwise needs a lot of care and done for the right reasons. There is no magic AI genie to make decisions for your specific requirements and circumstances.

Great post, George . We just finished doing a Bubble to Next.js/Supabase switchover.

Honest reason we moved: I’d always wanted to work in full stack but didn’t have the technical ability. AI changed that, and I love being able to build whatever I want without the platform limits. Which I realize puts me squarely in the profile of person you’re warning about — non-developer with AI tools deciding to migrate. The thing is, almost every piece of discipline in our migration was retrofitted after we did it the wrong way first. A few of our learnings:

We started building before we had a staging database — and it nearly sank the cutover. We dove straight into the new app, coding against one database, writing setup scripts we never replayed anywhere clean. The bill came due about four weeks in when we finally set up a second (staging) database: the scripts didn’t run in order. Some were numbered out of sequence, some referenced parts of the database that hadn’t been created yet, starter data pointed at fields that didn’t exist — all latent because nothing had ever been forced to run from scratch. By the day before cutover, staging and live had drifted so far apart that we had to rebuild the live database structure from a staging copy and write two cleanup scripts by hand to settle around 30 mismatches — rules, table connections, default values. We eventually added an automated check that compares the two databases before any change can ship, but retrofitting it after months of drift cost us weeks of rework and a white-knuckle 24 hours before launch. If I could give one piece of advice, it’d be: set up two synced databases and the discipline of staging-first promotion before you write a single feature. Doing it on day one would have been nearly free.

One thing Bubble enforces that AI doesn’t: environment separation. Bubble makes you develop in a sandbox and promote changes to live deliberately. AI is happy to operate against whatever you point it at, production included. We learned this when an AI agent linked itself to our live system and pushed five database changes I didn’t ask for. Nothing catastrophic survived, but it was the wake-up call — and the day after we wrote the safeguards we should have started with: automatic blockers on anything that touches the live database, the live deployment, or any live configuration. None of them existed before, and every one would have caught the push on its own.

Three things we did set up properly from day one, so this doesn’t read like total self-flagellation: we kept Bubble and the new stack fully separate (never wired one to the other), re-hosted every file off Bubble’s CDN so we weren’t still depending on it after “leaving,” and did the cutover the boring way — wrote down the old IP first, kept Bubble warm for 30 days as instant rollback, and decided in advance that payments and receipts trigger rollback while everything else gets fixed on the fly.

Anyway — agree with all of it. For our migration AI did the heavy lifting, but the guardrails were on us, and we built most of them only after we needed them. Wouldn’t change the move, but I’d do it in the order you describe next time.

So, as a 10+ year developer…

I’m wondering why you picked Next.js and Supabase?

What made you decide on those choices?

An inadequate search via Google and Reddit put them on my radar, and once I started building they were really easy to work with. That’s about it for process.

Genuinely curious what you’d have picked instead — if there’s a stack you’d point a non-dev toward, I’d want to know.

There are plenty of use cases where an external backend is the better option, not only for edge requirements.

The problem lies in how most developers go about relating their Bubble database with their external ones: There should only be one source of truth.

Keeping it all in Bubble is only feasible if you can pay up for a dedicated instance. Even then there are still many weaknesses that offloading will be a better choice.

Bubble can’t even handle concurrency properly.

Thanks. Was just wondering because I see Next.js/Supabase mentioned a lot on here.

I would think Laravel/Livewire would be more like Bubble.

If a native app is what someone’s after, I would think FlutterFlow + Firebase/Firestore would be the choice.

Each their own. Was just curious

@georgecollier - Is there any way to delete a conversation as it grows beyond…Currently only option is to archieve

Nah archive only atm