Bubble to Next.js: Complete Migration Guide
A complete guide to migrating a Bubble app to Next.js: auditing workflows, exporting data, mapping every workflow pattern to server actions, route handlers and cron jobs, rebuilding auth, and launching safely.
MigrateLab Team
Migration Experts

Why Next.js Is the Right Target for Bubble Apps
Bubble is not a website builder. It is a full application platform, with a database, workflows, user authentication and business logic all living inside a visual editor. That makes a Bubble migration different from a Framer or Webflow migration. You are not recreating a front end. You are rebuilding an entire application stack.
Next.js covers that whole stack in one project. React components handle the UI. Server actions and route handlers replace Bubble workflows. PostgreSQL (through Prisma or Drizzle) replaces Bubble's built-in database. Cron-triggered routes replace scheduled workflows. And it all deploys as one application in one Git repository, instead of five services stitched together.
The payoff is an app you own: server-rendered pages instead of Bubble's client-side runtime, hosting priced on compute instead of workload units, and a codebase that any developer, and any AI coding tool, can read, change and test.
Step 1: Audit Your Bubble Application
Before writing a line of code, you need a complete inventory of what your Bubble app does. Bubble apps accumulate complexity that is not visible from the front end: hidden workflows, conditional logic, scheduled tasks and API calls buried in the editor.
Create a document that covers:
Pages and reusable elements. List every page. Note which are public, which require login, and which read URL parameters. Document every reusable element, because each one becomes a React component.
Data types and fields. Every data type becomes a table and every field becomes a column. Pay special attention to fields that link to other data types: those become foreign keys.
Privacy rules. Write down every rule per data type. These are your authorization model, and they are easy to lose because they do not live on any page.
Workflows. The most important part of the audit. Go through every page and list every workflow (button clicks, page loads, input changes, custom events), plus every backend workflow, recurring event and database trigger. Write down what each one does, step by step, including every "Only when" condition.
API connections. List every call configured in the API Connector: endpoint, authentication method, and where the response is used.
Plugins. Document every plugin. Stripe, Google Maps and SendGrid have maintained JavaScript SDKs that are easier to work with than Bubble's plugin wrappers. Others represent custom behavior you will need to rebuild.
Bubble has no export for workflows or app logic, so this audit is manual. It is also the document every later step is checked against.
Step 2: Export and Migrate Your Data
Bubble lets you export each data type as CSV from the App Data tab:
Open the App Data tab in the Bubble editor
Select the data type
Click "Export" and download the CSV
Repeat for every data type, in both the live and development databases if you use both
The CSV includes Bubble's unique IDs, created and modified dates, and all field values. Watch for these gotchas:
Linked data types export as Bubble IDs. If an "Order" has a "Customer" field that links to "User", the CSV holds the Bubble unique ID of that user, not their email. Keep the Bubble ID as a column during import and map it to your new primary keys.
Lists export as comma-separated values. List fields (tags, categories, members) land as one comma-separated string in a single cell. Parse them into junction tables for proper many-to-many relationships.
File and image fields export as URLs. These point to Bubble's file storage. Download every file before you cancel your Bubble plan, because those URLs are not yours to keep.
Geographic addresses are composite. Bubble stores a formatted address plus latitude and longitude. Decide up front whether that becomes separate columns or a JSONB field.
Empty is ambiguous. Decide how an empty text field maps (NULL or empty string) and apply it consistently, or it will surface later as subtle query bugs.
Setting Up PostgreSQL
Design your schema from your Bubble data types. Each field maps to a PostgreSQL type:
Bubble text → TEXT
Bubble number → INTEGER, BIGINT or NUMERIC (use NUMERIC for money)
Bubble yes/no → BOOLEAN
Bubble date → TIMESTAMP WITH TIME ZONE
Bubble file/image → TEXT (the URL after you re-host the file)
Bubble geographic address → JSONB or separate lat/lng columns
Bubble list → ARRAY, or a junction table for many-to-many
Bubble linked data type → FOREIGN KEY reference
Define the schema in TypeScript with Prisma or Drizzle and generate migrations from it. You get type-safe queries everywhere in the app, which Bubble's untyped expressions never gave you.
Step 3: Recreate the UI with React Components
Bubble's visual elements map cleanly to React:
Group → div with Flexbox or Grid. Alignment, padding and gap settings translate directly to CSS.
Repeating Group → Array.map() over query results. Pagination, filtering and sorting move into your database query.
Text → semantic HTML (h1 to h6, p, span). Dynamic expressions become props or template literals.
Input → controlled inputs or forms posting to server actions. Use React Hook Form or Zod-validated server actions for multi-step forms and validation.
Popup → modal component built with Radix, Headless UI or a portal.
Reusable Element → React component, with props replacing the element's custom states.
Handling Responsive Design
Bubble's responsive engine is constraint-based, with minimum and maximum widths that often behave unpredictably. Tailwind's mobile-first breakpoints are more predictable: define the mobile layout, then add modifiers for tablet and desktop.
Step 4: Convert Workflows to Code
This is the heart of the migration. A single Bubble workflow often mixes a database write, a condition, an API call, an email and a redirect in one visual sequence. In code, those concerns separate into layers: validation, a service function, the ORM, an SDK call, the router. That separation is what makes the result testable.
Here is the pattern map. Each Bubble construct has one sensible home in Next.js.
1. "When Button is clicked" → Server Action
A click that creates or changes data becomes a server action: a function that runs on the server, validates its input and writes to the database.
``ts 'use server' export async function createTask(formData: FormData) { const user = await requireUser() const input = TaskSchema.parse({ title: formData.get('title'), description: formData.get('description'), }) return db.task.create({ data: { ...input, assignedToId: user.id } }) } ``
TaskSchema is a Zod schema and requireUser reads the session. Clicks that only change the UI (show a group, toggle a state) stay in the client as plain event handlers.
2. "Only when" conditions → if/else on the server
Conditions attached to workflow steps become ordinary conditionals or a switch. One rule matters here: anything that controls access (role checks, ownership checks) must run on the server. In Bubble a hidden button looks like security. In code, the server action has to check again, because anyone can call it directly.
3. "Do a search for" on page load → Server Component
A Repeating Group bound to a search becomes a server component that queries the database before the page renders, so the HTML arrives with the data in it.
``tsx export default async function ProjectsPage() { const user = await requireUser() const projects = await db.project.findMany({ where: { creatorId: user.id }, orderBy: { createdAt: 'desc' }, }) if (projects.length === 0) return <p>No projects yet</p> return <ul>{projects.map((p) => <li key={p.id}>{p.name}</li>)}</ul> } ``
Page-load redirects ("if not logged in, go to login") belong in middleware or at the top of the server component.
4. Backend workflows and recurring events → Route Handlers plus Cron
Backend workflows exposed as API endpoints become route handlers in app/api/.../route.ts. Recurring events become a route handler triggered on a schedule: Vercel Cron (a crons entry in vercel.json), your host's scheduler, or an external cron service. Protect the route with a secret so only the scheduler can call it, and make it idempotent, so a retried run does not email the same user twice.
"Schedule API workflow" at a future time (send a reminder in three days) is a different thing. It needs a job queue or a jobs table with a run-at column that a cron route drains, not a fixed schedule.
5. API Connector → server-side fetch or the official SDK
Each API Connector call becomes a small typed function that runs on the server, with keys in environment variables instead of the browser. Where the provider has an official SDK (Stripe, Resend, Slack), use it: authentication, retries and typed responses come built in.
6. Custom events → plain functions
A custom event is Bubble's version of a function, so it becomes one. A "Process Order" event triggered from several places becomes processOrder(orderId), called by every action that needs it. Wrap its database writes in a transaction so they all succeed or all roll back, which Bubble's step-by-step execution never guaranteed.
7. Database trigger events → explicit side effects
"When an Order is created, notify the admin and update the metrics" is clearest as code inside your createOrder service function, right after the insert. ORM query hooks (Prisma client extensions, for example) can intercept writes, but they hide side effects where nobody looks for them. If several features react to the same event, emit an order.created event and give each reaction its own handler.
Convert in this order
Core user flows: sign up, log in, and the main action your users take (create a project, place an order, book a slot).
Data mutations: everything that creates, updates or deletes records.
Notifications: emails, in-app alerts, outgoing webhooks.
Scheduled and background work: recurring jobs, cleanups, subscription expiry. Add error monitoring here, because these run unattended and fail silently.
For each workflow: read the Bubble steps from your audit, write the code, run it against real exported data, and compare the result with what Bubble produced for the same input. Keep Bubble running in parallel so you can check side by side.
Step 5: Rebuild Authentication
Bubble handles sign up, log in, password reset and email verification for you. In Next.js you pick a library:
Auth.js (NextAuth): email and password, OAuth providers, magic links and sessions. Covers most Bubble apps.
Clerk: managed authentication with pre-built UI, multi-factor authentication and organizations.
Better Auth or a hand-rolled session layer: database-backed auth when you want full control alongside Prisma or Drizzle.
Plan the user move carefully. Bubble does not export password hashes, so existing users must set a new password. The usual approach is a "welcome to the new app" email with a reset link on launch day, or a first-login flow that verifies the email and asks for a new password.
Step 6: Handle Bubble-Specific Patterns
Privacy Rules → Authorization Checks
Privacy rules decide who can see and change which data. In code, the simplest version is an authorization check in every server action and query. For complex multi-tenant apps, PostgreSQL row-level security enforces the same rules in the database itself.
Custom States → React State
Custom states are element-level variables. They become useState hooks, or a small store with Zustand or Jotai when several components share them.
URL Parameters → Dynamic Routes
Bubble passes data between pages with URL parameters. In Next.js, dynamic segments ([slug], [id]) carry the identity of the page, and searchParams carry filters and sort order. Map any changed URL with a 301 redirect.
Testing and Launch Checklist
Before switching from Bubble to your Next.js app, verify:
Every page renders correctly on mobile, tablet and desktop
Registration, login and password reset work end to end
All data imported correctly, with relationships intact and row counts matching the CSVs
Every workflow from the audit has been converted and produces the same result
File uploads work and previously uploaded files load from their new location
Transactional emails send correctly
Payments work, including webhooks from your payment provider
External API integrations respond correctly
Key pages are measurably faster: run Lighthouse and compare with your Bubble baseline
301 redirects are in place for every changed URL
Keep your Bubble app running for at least 30 days after launch as a safety net. If a critical issue surfaces, you can point the domain back while you fix it.
Want a fixed price for your app before you commit? Bubble apps are scoped by workflows and data model rather than page count, so we price them after a free migration review. For a real example of the full process, read the Bubble to Next.js case study.
| Feature | Bubble Element | Next.js Equivalent |
|---|---|---|
| Group / Container | Visual layout container | div + Flexbox/Grid + Tailwind |
| Repeating Group | Visual data list | Array.map() + React components |
| Popup | Built-in modal | Headless UI / Radix modal |
| Input / Form | Basic form fields | React Hook Form + validation |
| Workflows | Visual action sequences | API routes + server actions |
| Database | Built-in (no SQL, limited) | PostgreSQL + Prisma (full SQL) |
| Authentication | Built-in (basic) | NextAuth / Clerk (OAuth, MFA) |
| Privacy Rules | Visual rule builder | Row-level security / middleware |
6-Step Bubble to Next.js Migration Process
Audit your Bubble application
Document every page, data type, workflow, API connection, and plugin. Take screenshots of every workflow sequence. This inventory becomes your migration roadmap.
Tip: Export your Bubble app's workflow list by going to each page and documenting the workflow tab. There is no bulk export, so this step requires manual review.
Export and migrate your data
Export CSV files for every data type from the App Data tab. Design your PostgreSQL schema, set up Prisma or Drizzle, and write import scripts that map Bubble IDs to new database IDs.
Tip: Download all files and images referenced in your data before canceling your Bubble plan, those S3 URLs will stop working.
Build the UI with React components
Translate Bubble pages and reusable elements into React components with Tailwind CSS. Start with the most reused components: navigation, cards, modals, and form elements.
Tip: Use Tailwind's mobile-first breakpoints to fix the responsive design issues that Bubble's constraint system likely caused.
Convert workflows to code
Transform Bubble workflows into API routes, server actions, and event handlers. Map each workflow step to its code equivalent: database writes, API calls, conditional logic, and email sends.
Tip: Start with the most critical user flows (sign up, core feature, payment) and work outward to less-used features.
Implement authentication
Set up NextAuth.js or Clerk for user authentication. Plan a password reset flow for existing users since Bubble password hashes cannot be exported.
Tip: Send a "Welcome to the new platform" email with a password reset link to all users on launch day.
Test, verify data integrity, and launch
Run through every user flow end-to-end. Verify data imports, test payment processing, check email notifications, and run Lighthouse audits. Keep Bubble running for 30 days as a fallback.
Tip: Create test accounts that mirror your real users' data to catch edge cases in workflows and permissions.
Need help migrating from Bubble? We handle the technical heavy lifting so you can focus on your business.
Frequently asked questions
- Can I export my Bubble workflows to code?
- No. Bubble exports data (CSV per data type, or through the Data API if you enable it) but not workflows, conditions or page logic. You document every workflow by hand during the audit and rewrite it. Each Bubble construct has a clear home in Next.js: clicks become server actions, searches become server component queries, backend workflows become route handlers, recurring events become cron-triggered routes.
- Will my users have to reset their passwords?
- Yes. Bubble does not export password hashes, so accounts move over without passwords. Send a welcome email with a reset link on launch day, or build a first-login flow that verifies the email address and asks for a new password.
- Which Bubble workflows should I convert first?
- Start with the flows users touch most: sign up, log in, and the main action of your app. Then everything that writes data, then notifications, then scheduled and background jobs. Test each one against real exported data and compare the output with Bubble before moving on.
- What replaces Bubble's scheduled workflows?
- Recurring events become a route handler triggered on a schedule, for example by Vercel Cron or your host's scheduler. One-off scheduled workflows (send a reminder in three days) need a job queue or a jobs table drained by a cron route. Protect cron routes with a secret and make them safe to run twice.
- Where do Bubble privacy rules go in Next.js?
- Into authorization checks on the server: every server action and query checks that the current user may read or change that record. For multi-tenant apps, PostgreSQL row-level security can enforce the same rules in the database. Never rely on hiding a button, because server actions can be called directly.
- How much does a Bubble to Next.js migration cost?
- It depends on workflow count, data model and integrations more than on pages, so Bubble apps are priced after an audit. MigrateLab gives you a fixed price from a free review before you commit, and you receive the full Git repository at handover.
100 website designs, ready to build for your business
Every design below is a complete, clickable website. Pick one and we rebuild it around your name, colours, photos and services, from $1,520 and live in 7 working days.
See all 100 designsRelated Resources

Bubble Database to PostgreSQL: Migration Walkthrough
A step-by-step walkthrough for migrating your Bubble database to PostgreSQL, covering data export, schema design, type mapping, relationship handling, and data validation.

Bubble vs Custom Code: Performance, Cost, and Control
Bubble versus custom code compared on build speed, performance, cost at scale, customization, AI tooling and ownership, including when staying on Bubble is the right call.