Skip to content
BlackFig

Turning a no-code app into a coded product

AntPublished Updated 5 min read

Quick answer

You can't export a Bubble app as code, so migrating means rebuilding the app in a coded stack and moving the data across. Export your data with Bubble's CSV or JSON export or its Data API, map each data type and workflow to a table and server function, then switch users over in one planned cutover.

On this page
  1. What moves and what doesn't
  2. Mapping Bubble to code
  3. Migrating the data
  4. Users, sign-in and the cutover
  5. Real examples
  6. When it's worth doing
  7. Where I fit

The first thing to know is that nothing converts. Bubble's own support article says there is currently no way of exporting your application as code. What you can take with you is your data, plus, if you leave the platform, a raw JSON dump of your app's logic and visual design that Bubble can provide to help with conversion.

So a Bubble migration is a rebuild with a head start. The head start is big: you already know what users do, which features matter and where the app falls over. That makes the coded version faster to scope than a fresh MVP. If you're still deciding whether to move at all, read my comparison of no-code and custom code for an MVP first.

What moves and what doesn't

Data moves. Bubble's manual lists CSV, JSON and newline-delimited JSON (NDJSON) as export formats from the Data, App data tab. You pick a data type, set up a view, click Export, and Bubble emails a download link. Exports share the scheduler queue with API workflows, so a busy app can delay them.

The Data API gives you the same records over HTTP. A list request looks like GET https://appname.bubbleapps.io/api/1.1/obj/typename, paged with cursor and limit, and each response reports a count and how many records are remaining. Records come sorted by creation date by default, and each one carries an _id and a Created Date. The manual states a limit of 50,000 items in any GET request, or 10,000,000 on Enterprise plans, so very large tables need the editor export or several passes.

Logic does not move. Workflows, page layouts, privacy rules, plugins and API Connector calls all get rebuilt. One small detail worth knowing: date interval fields export as descriptive text such as "2 days", even though Bubble stores them as milliseconds, so convert them on the way in.

Mapping Bubble to code

This is how the main Bubble concepts map to code. The right-hand column assumes my usual stack: Next.js, TypeScript and Postgres.

In BubbleIn the coded product
Data typeA Postgres table with real column types and constraints
A thing's unique idKept as a bubble_id column during the move, then dropped
Field that is a list of thingsA join table, or a foreign key on the child table
Option setA Postgres enum or a small lookup table
Privacy rulesRow Level Security policies or checks in server code
Page workflowA server action or API route
Backend workflow or scheduled workflowA cron job or a queue worker
API Connector callA server-side fetch with secrets in environment variables
PluginAn npm package or a direct call to the provider's API
Reusable elementA React component

Two rows deserve care. Privacy rules are your security model, so list every one before you start and write a test for each. List fields hide relationships, and turning them into proper foreign keys is where most of the data cleaning happens.

Migrating the data

I treat the data move as a script I can run many times, not a one-off. You run it during development to get realistic data, then again at cutover.

Step one is pulling each data type out of Bubble into a file:

// scripts/export-bubble.ts
// Pages through one Bubble data type with the Data API and writes NDJSON.
// Sends an admin API token as a Bearer token, as Bubble's authentication guide describes.
import { appendFileSync, writeFileSync } from "node:fs";
 
const APP = process.env.BUBBLE_APP!; // "yourapp" for yourapp.bubbleapps.io
const TOKEN = process.env.BUBBLE_API_TOKEN!; // an admin API token from the app's API settings
const TYPE = process.argv[2] ?? "user";
const LIMIT = 100;
 
async function main() {
  const out = `export-${TYPE}.ndjson`;
  writeFileSync(out, "");
  let cursor = 0;
  let remaining = 1;
 
  while (remaining > 0) {
    const url = `https://${APP}.bubbleapps.io/api/1.1/obj/${TYPE}?cursor=${cursor}&limit=${LIMIT}`;
    const res = await fetch(url, { headers: { Authorization: `Bearer ${TOKEN}` } });
    if (!res.ok) throw new Error(`Bubble Data API ${res.status}: ${await res.text()}`);
 
    const { response } = await res.json();
    for (const row of response.results) appendFileSync(out, JSON.stringify(row) + "\n");
 
    cursor += response.count;
    remaining = response.count === 0 ? 0 : response.remaining;
    console.log(`${TYPE}: ${cursor} exported, ${remaining} remaining`);
  }
}
 
main();

Step two is a schema that keeps the old ids while you migrate, so relationships can be rebuilt and the import can be re-run safely:

create table listings (
  id uuid primary key default gen_random_uuid(),
  bubble_id text unique,               -- Bubble's _id, kept until cutover is done
  owner_id uuid not null references profiles (id),
  title text not null,
  status text not null default 'draft',
  created_at timestamptz not null      -- from Bubble's "Created Date"
);
 
-- Postgres doesn't index foreign keys for you.
create index listings_owner_id_idx on listings (owner_id);

The import then upserts on bubble_id, looks up each owner's new id from their old Bubble id, and logs every row it couldn't place. That log is the real output: it shows you the orphaned records and messy text fields that years of no-code editing leave behind.

Files need their own pass. Check where your uploaded images and documents live, copy them into your new storage, and rewrite the URLs in the database.

Users, sign-in and the cutover

Users are the part founders worry about most, and rightly. I haven't found Bubble documentation that offers password hashes for export, so I plan for every user to set a new password or sign in with a magic link the first time they use the new app. An email a week before, and another on the day, keeps support quiet.

The cutover itself is short if the work before it is done:

  1. Freeze

    Show a maintenance page in the Bubble app or switch off the forms that write data, so nothing new arrives mid-move.

  2. Final sync

    Run the export and import scripts one last time and check row counts per table against Bubble.

  3. Switch the domain

    Point DNS at the new host and add redirects for any old URLs that people have bookmarked or search engines have indexed.

  4. Watch

    Keep the Bubble app running out of public view for a few weeks, in case you need to check an old record.

Real examples

FindPopups, a two-sided marketplace I designed and built for a client, has the kind of features that tend to justify a coded build: a live map, vendor and host dashboards, and an AI schedule assistant. Those are the same areas where no-code platforms usually strain: complex permissions, maps and search at speed, and background jobs that run without a page open.

When it's worth doing

Moving off Bubble is worth it when the platform has become the constraint, not because code is fashionable. The signals I look for:

  • The product works. People use it and pay for it, so you're rebuilding something proven, not guessing.
  • Usage costs keep climbing. Bubble measures usage in workload units, with a monthly allowance per plan and overages or extra workload tiers beyond it, so success shows up on the bill.
  • You need what the platform does badly. Complex permissions, heavy data, background jobs, native-feeling mobile, or integrations with no plugin.
  • Investors or buyers ask what you own. Code in a repository in your name is an asset. An app that only runs on someone else's platform is harder to value.

If none of those apply yet, stay put and keep learning from users. When they do, start with a written spec of what the Bubble app does today. My product spec template works well for this, and the guide to exporting AI-built code covers the ownership side once you're on a coded stack. The bigger picture of going live lives in my prototype to product guide.

Where I fit

I'm a senior product designer who writes the code, so the rebuild is also a chance to fix the interface that grew one workflow at a time. The MVP Build starts at £12,000 and typically takes 4 to 6 weeks, depending on scope, with the repository, hosting and database in your name. If you want the migration planned before committing, the Validation Sprint (from £3,500, 1 to 2 weeks) maps the user flows, produces a clickable prototype and ends with a build plan and fixed quote. Book a call and bring your Bubble editor link.

The launch checklist for founders, as a PDF

About 40 checks across legal, payments, domain and email, analytics, backups and support.

You get the PDF now and two short follow-ups over the next week. Reply “stop” and the rest never arrive.

Questions founders ask

Can I export my Bubble app as code?

No. Bubble's own support article says there is currently no way of exporting an application as code. You can export your data, and if you leave the platform Bubble can provide a raw JSON dump of your app's logic and design to help with conversion.

How do I get my data out of Bubble?

Two ways. The editor's App data tab exports a data type as CSV, JSON or NDJSON. The Data API returns records as JSON over HTTP, which is easier to script and repeat during a cutover.

Will my users have to reset their passwords?

Plan for it. I haven't found Bubble documentation that offers password hashes for export, so I build the new app with a password reset or magic link on first sign-in, and email users before the switch.

How long does a Bubble to code migration take?

It depends on how many pages, workflows and integrations the app has. A focused app usually fits the 4 to 6 weeks of an MVP Build, because it is effectively a rebuild with the spec already proven by real users.

Ant

Product designer who builds MVPs, based in Nottingham. 10+ years designing digital products, and the only person at BlackFig. More about me