Vercel is the natural landing pad for an app that started on base44. Both lean React-first. Both prioritize fast deploys and zero-config hosting. The difference is that Vercel gives you an actual platform: previews, edge functions, environment variables that work, real CI, and a build log you can read.
This playbook covers the Vercel-specific work. If you have not picked a database yet, the Next.js + Supabase migration is the more opinionated path. Read this one if you already know your backend choice and want to focus on hosting and frontend cutover.
Why migrate to Vercel
Three reasons drive this migration in the order we see them:
- SSR and SEO. Base44 is CSR-only, so Google sees an empty
<div id="root">. Most marketing sites and content-heavy apps cannot get indexed properly. Next.js on Vercel ships SSR or SSG by default, and the SEO penalty disappears once you redeploy. - Real CI/CD. Vercel's preview deploys per pull request are an order-of-magnitude productivity upgrade over base44's "publish" button. You see the diff. You share a URL with stakeholders. You revert atomically.
- Edge performance. Vercel's edge runtime puts your code in 30+ regions. Base44 runs out of one region. For global apps, latency drops by 100–300ms post-migration, no code change required.
You give up base44's AI agent, the in-browser editor, and the prompt-driven development loop. If those are your edge, this migration costs you that. If not, it pays back fast.
What you keep, what you rebuild
| Layer | What you keep | What you rebuild |
|---|---|---|
| React components | 85–95% (JSX, Tailwind, hooks) | Anything calling @base44/sdk |
| Page routing | URL structure | Re-implement on Next.js App Router |
| Static assets | Images, fonts, files | Re-upload to Vercel public dir or your CDN |
| Environment variables | Names | Re-add via Vercel dashboard or CLI |
| Backend functions | Function bodies (mostly) | Wrap as Next.js Route Handlers |
| Database | Data | Schema + queries (depends on chosen DB) |
| Auth | User identifiers | Sessions (force re-issue) |
| Webhooks | Endpoint URLs (you must update senders) | Handler code |
| Scheduled tasks | None | Build with Vercel Cron or external scheduler |
| Domain + SSL | Domain | Reconfigure on Vercel (free SSL via Let's Encrypt) |
| Analytics | None | Vercel Analytics, Plausible, or Google Analytics |
The frontend ports cleanly. The backend layer between your React components and your data is what burns the time.
Architecture: source vs target
Base44 (current):
[browser] → CSR React (base44 hosted)
↓
@base44/sdk
↓
base44 backend (DB + Functions + Auth)
↓
single region, no SLA, opaque logs
Vercel + your backend (target):
[browser] → Next.js (SSR / RSC / Edge)
↓ ↓
app/api/* Route Handlers Edge Functions
↓ ↓
Supabase / Neon / PlanetScale / your DB
↓
multi-region, real logs, real CI, real SLA
The shape changes from "client-side React calling a managed SDK" to "server-rendered React calling your own backend." That shift is what unlocks SSR, SEO, real-time, and proper observability.
Step-by-step migration plan
Phase 1 — Discovery (Week 1)
1. Export your base44 app
If you are on a paid plan, base44 supports GitHub export. The feature is still in beta and skips backend functions on some accounts. See our export code guide for the details.
# Once you have the export
git clone https://github.com/yourorg/your-base44-app.git
cd your-base44-app
npm install
npm run build # this will fail; the SDK won't connect from outside base44
You expect the build to fail or partially succeed. The point is to have the source code on disk.
2. Pick your backend stack
Decide before you start. Switching mid-migration costs two to four weeks.
| Backend | Best for | Trade-off |
|---|---|---|
| Supabase | Postgres + Auth + Storage + Realtime | Vendor lock-in to Supabase |
| Neon | Pure Postgres with branching | You build auth and storage yourself |
| PlanetScale | MySQL with branching | No Postgres features (RLS, pgvector) |
| MongoDB Atlas | Document data | Different query model than base44 |
| Self-hosted Postgres | Full control | You operate infra |
Most migrations off base44 land on Supabase. We default to it unless the team has a strong reason otherwise.
3. Audit your SDK calls
grep -rn "base44\." src/ | tee migration/sdk-calls.txt
wc -l migration/sdk-calls.txt
This number is your effort estimate. Expect 50–600 calls depending on app size.
Phase 2 — Project setup (Week 1–2)
4. Stand up a clean Next.js project
Do not try to "convert" the export in place. Start a new Next.js project and port files into it.
npx create-next-app@latest my-app --typescript --tailwind --app --src-dir
cd my-app
npm install @vercel/analytics @vercel/speed-insights
Copy your component files (anything under src/components, src/pages from the export) into the new structure. Skip anything in the src/integrations/ folder if base44 generated one — those are SDK adapters you will replace.
5. Configure Vercel
npm install -g vercel
vercel link
vercel env pull .env.local
Set up production, preview, and development environments separately. Add your database URL, auth secrets, and third-party API keys.
6. Migrate routing to App Router
Base44's exported code uses React Router or its own routing layer. Convert to Next.js App Router conventions.
// before: src/pages/dashboard.tsx (React Router)
export default function Dashboard() {
const projects = useBase44(b => b.entities.Project.find());
return <ProjectList projects={projects} />;
}
// after: app/dashboard/page.tsx (Server Component)
import { createClient } from "@/lib/supabase/server";
export default async function DashboardPage() {
const supabase = createClient();
const { data: projects } = await supabase.from("projects").select("*");
return <ProjectList projects={projects ?? []} />;
}
The conversion is mostly mechanical. Server Components fetch data inline. Client Components keep React hooks.
Phase 3 — Backend rewrite (Weeks 2–4)
7. Port backend functions to Route Handlers
Each base44 backend function becomes one file under app/api/.
// app/api/create-invoice/route.ts
import { NextResponse } from "next/server";
import Stripe from "stripe";
import { createClient } from "@/lib/supabase/server";
export const runtime = "nodejs"; // or "edge" for Stripe webhooks
export async function POST(req: Request) {
const { project_id, amount } = await req.json();
const supabase = createClient();
const stripe = new Stripe(process.env.STRIPE_SECRET!);
const invoice = await stripe.invoices.create({ customer: "...", auto_advance: true });
await supabase.from("invoices").insert({ project_id, stripe_id: invoice.id, amount });
return NextResponse.json({ id: invoice.id });
}
Pick runtime = "edge" for fast, lightweight handlers. Use nodejs runtime for anything that needs the full Node API (file system, native modules, longer execution time).
8. Replace SDK calls in components
Mechanical work. For each base44.entities.X call:
// before
const tasks = await base44.entities.Task.find({
filter: { projectId },
sort: { createdAt: -1 },
});
// after (Server Component)
const { data: tasks } = await supabase
.from("tasks")
.select("*")
.eq("project_id", projectId)
.order("created_at", { ascending: false });
Do this in batches by route. Ship behind a feature flag (NEXT_PUBLIC_USE_NEW_BACKEND=true). Compare data parity before flipping.
9. Wire auth
Pick your auth provider. Supabase Auth, Clerk, NextAuth, and Auth.js all work cleanly on Vercel.
// middleware.ts (Supabase example)
import { createServerClient } from "@supabase/ssr";
import { NextResponse, type NextRequest } from "next/server";
export async function middleware(req: NextRequest) {
const res = NextResponse.next();
const supabase = createServerClient(/* ... */);
const { data: { session } } = await supabase.auth.getSession();
if (!session && req.nextUrl.pathname.startsWith("/dashboard")) {
return NextResponse.redirect(new URL("/login", req.url));
}
return res;
}
Force every existing user to reset their password via magic link. Base44 does not export password hashes.
Phase 4 — Cron and scheduled jobs (Week 4)
10. Configure Vercel Cron
// vercel.json
{
"crons": [
{ "path": "/api/cron/daily-digest", "schedule": "0 9 * * *" },
{ "path": "/api/cron/cleanup", "schedule": "0 3 * * *" }
]
}
// app/api/cron/daily-digest/route.ts
import { NextResponse } from "next/server";
export async function GET(req: Request) {
const auth = req.headers.get("authorization");
if (auth !== `Bearer ${process.env.CRON_SECRET}`) {
return NextResponse.json({ error: "unauthorized" }, { status: 401 });
}
// your job logic
return NextResponse.json({ ok: true });
}
Vercel Hobby caps cron at daily; Pro allows minute-level. For high-frequency jobs, layer Inngest or QStash on top.
Phase 5 — Deployment + cutover (Weeks 5–6)
11. Configure your domain on Vercel
Add the domain in Vercel's dashboard. Update DNS to point at cname.vercel-dns.com. Vercel issues SSL automatically. Keep base44's domain pointed at base44 until cutover is complete.
12. Dual-run window
Deploy your Vercel app on a staging subdomain (new.yourapp.com). Pipe a small percentage of traffic to it via your DNS or a reverse proxy. Monitor errors. Compare data writes between base44 and Vercel.
// during dual-run
async function createProject(payload) {
const [b44, vercel] = await Promise.allSettled([
base44.entities.Project.create(payload),
fetch("https://new.yourapp.com/api/projects", {
method: "POST",
body: JSON.stringify(payload),
}),
]);
if (b44.status === "rejected" || vercel.status === "rejected") {
await alertSlack("dual-write divergence", { b44, vercel });
}
return b44.value;
}
13. DNS cutover
When error rates and data parity look clean, swap DNS. Lower the TTL to 60 seconds twenty-four hours ahead of time. Then point the apex at Vercel. Total downtime: under five minutes if you do this on a Tuesday morning at 3am local time.
Phase 6 — Sunset (Weeks 6–8)
14. Read-only base44 + decommission
Lock the base44 app to read-only. Take a final export. Cancel the base44 plan after thirty days of stable production on Vercel.
Common pitfalls
1. Trying to deploy the export directly. It compiles, sometimes. It never runs against real data without an SDK rewrite. Plan to rewrite the data layer before deploying anything.
2. Edge runtime incompatibility. Some npm packages (Stripe SDK, certain database clients) do not run on Edge. Default to runtime = "nodejs" and only switch to edge for hot paths.
3. Environment variables forgotten. Base44 hides secrets in its own UI. Inventory every secret before cutover. Add them to Vercel for production, preview, and development separately.
4. Cron secret missing. Vercel Cron hits your endpoint with no built-in auth. Use a CRON_SECRET env var and check it in every cron route or anyone can hit /api/cron/* and trigger your jobs.
5. URL structure changes wipe SEO. If your URLs change shape from /app/dashboard to /dashboard, you lose Google's accumulated authority. Map every old URL to a new one and ship 301 redirects in next.config.js or vercel.json.
6. Webhook senders not updated. Stripe, Resend, GitHub, and any other webhook source still points at the old base44 URL. Update each one at the cutover moment, not before.
7. Vercel function size limits. Lambdas on Vercel have a 50MB compressed size limit. If your dependencies bloat past that, split into multiple smaller functions or move heavy compute to a separate service.
Timeline + team
Four to ten weeks with this team:
- One senior frontend engineer. Owns the export-to-Next.js port. Forty hours per week.
- One backend engineer (or the same person, if senior). Owns the data-layer rewrite. Twenty to forty hours per week.
- One ops/DNS person. Owns the cutover. Eight hours total over the project.
You can do it solo if you are a senior full-stack engineer and the app is small. Two engineers is the sweet spot for medium apps. Beyond that, parallelize by route.
Cost
Migration tiers from our practice:
| Tier | Price | What you get |
|---|---|---|
| Small | $6,000 | 4–6 weeks, simple app, single backend, standard auth |
| Medium | $12,000 | 6–10 weeks, custom integrations, complex auth, cron jobs |
| Enterprise | $25,000+ | 10+ weeks, compliance constraints, white-glove cutover, on-call support |
Vercel infrastructure: Vercel Pro at $20/user/mo. Database: $25–$100/mo depending on choice. Total ongoing cost: $50–$300/mo for most apps.
DIY cost is engineering time. Two senior engineers for six weeks is roughly $40,000–$60,000 in fully-loaded salary. Hiring the migration is usually cheaper if you do not already have the team.
DIY vs hire decision
DIY this if:
- Your team has Next.js and your chosen backend on staff already.
- Your app has under thirty entities and standard auth.
- You can run a six-to-eight-week project without losing focus on other priorities.
Hire help if:
- You need to be live on the new stack in under six weeks.
- Your app has paying users and you cannot risk a botched cutover.
- Your team is base44-only and has never shipped a Next.js app to production.
- You tried the export, stared at the code, and realized you do not know where to start.
Want a free migration assessment?
Tell us what your app does and we will scope it. Free thirty-minute call, fixed-price quote within twenty-four hours.
Book a free migration assessment
Related migrations
- Base44 to Next.js + Supabase — the opinionated stack if you have not picked a backend yet.
- Base44 to self-hosted — full ownership, your own infrastructure.
- Base44 export code guide — how the export feature actually works.