Skip to content
Back to journal
Next.jsWeb to AppAndroid

Convert a Next.js App to a Mobile App Without Losing SSR (2026)

CCode2Native EngineeringEngineering team
Published
Updated

There are two honest readings of “convert Next.js to a mobile app.” One is a rewrite: port the UI to React Native or Flutter and maintain two codebases forever. The other starts from what a Next.js app already is: a server, deployed and running, that any Chromium-based browser can render. An Android WebView is one of those browsers.

This post is about the second reading. Code2Native takes your production URL — on Vercel or self-hosted, it doesn't matter — and wraps it in a native Android shell: your icon, your splash screen, OneSignal push, compiled and signed into an APK/AAB in a few minutes. To be precise about what that is: a native shell rendering your live deployment, not a native rewrite. Your server components stay on your server. Your app store listing points at your CI/CD pipeline.

The rest of this guide is the Next.js-specific detail: what your middleware does to an unfamiliar client, how ISR timing shows up on installed phones, and the one genuine trap — third-party OAuth — that catches Next.js apps more than any other stack.

Why you wrap the URL, not the repo

A plain React SPA is a folder of static files — you can physically copy it into a Capacitor shell and ship it on the device. We cover that route in the React version of this guide. A Next.js app is not that. It's a running system: server components render on the server, middleware sits at the network boundary, ISR regenerates pages on the host, API routes and server actions execute next to your database.

The one official way to freeze Next.js into static files — output: "export" — disables exactly the features you chose Next.js for: no SSR, no middleware, no ISR, no API routes, and next/image needs a custom loader. That's why the Capacitor-with-static-export route, which works fine for Vite SPAs, is usually a downgrade for Next.js apps (our Capacitor vs native comparison is the longer discussion). Wrapping the deployed URL keeps the whole stack intact because the phone is just another client of your deployment.

Mechanically, our pipeline generates a real Android project around a Chromium WebView pointed at your URL, layers in the native pieces — icon, splash, package ID, OneSignal push — and compiles and signs an APK and a Play-ready AAB in the cloud with a per-project keystore, typically in a few minutes. That's the whole trick; the full technical rundown lives in the docs.

What survives the wrap: a Next.js feature audit

Evaluate the wrapper like any new client of your API — feature by feature:

Next.js featureIn the wrapped appWhy
SSR & Server ComponentsWorks unchangedRendering happens on your server; the app is just another client
App Router / Pages Router navigationWorks unchangednext/link pushes History API entries; the Android back button steps through them
Middleware / proxy.tsWorks — audit itRuns server-side on every request the app makes; see the preflight below
ISR & cachingWorks unchangedUpdates reach installed phones on your revalidate schedule, no store review
next/imageWorks — check config/_next/image is same-origin; remote hosts must be in remotePatterns
API routes & server actionsWorks unchangedSame-origin fetches with cookies, exactly as in mobile Chrome
Third-party OAuth (Google, GitHub)Needs real careGoogle blocks its OAuth endpoint inside embedded WebViews — full section below
Offline modeNot a wrapper featureThe app renders your deployment; no connection, no app

Four deployment checks before you spend the free build

1. Wrap the production domain — never a *.vercel.app preview URL

As of mid-2026, new Vercel projects ship with Deployment Protection enabled, which puts Vercel Authentication in front of preview URLs by default. Wrap a preview URL and your shiny new app opens to a Vercel login wall. Production domains stay public, so point the wrapper at https://yourapp.com. And if your app is served under a basePath (say /app), give the wrapper the full URL including that path — internal next/link navigation already stays under it, so nothing else changes.

2. Audit your middleware — or proxy.ts, post-Next 16

Since Next.js 16, middleware.ts is deprecated in favor of proxy.ts (there's a codemod: npx @next/codemod@latest middleware-to-proxy .). Whatever yours is called, it runs on every request the app makes — read it once with a WebView client in mind. The usual suspects: geo or locale redirects that bounce users to a country subpath on first load, auth guards that redirect to /login and back, and user-agent sniffing that lumps unfamiliar UA strings in with bots. A redirect chain a desktop browser shrugs off is very visible as the first thing your app does after the splash.

3. Check remotePatterns if you load remote images

next/image works fine in the app — the optimizer endpoint is same-origin, so there's nothing WebView-specific about it. The failure mode is the one you know from the browser: a remote host missing from your config returns a 400 and a broken image — identically broken in the app. If your content images come from a CMS or object storage, make sure the allowlist covers them:

// next.config.ts
images: {
  remotePatterns: [
    { protocol: "https", hostname: "images.ctfassets.net" },
    { protocol: "https", hostname: "*.supabase.co" },
  ],
},

Fix it once at the source and both web and app are fixed.

4. Know your ISR windows

When a route uses revalidate, Next.js emits s-maxage plus stale-while-revalidate directives that Vercel's CDN consumes, serving cached HTML until the window expires. For the app this is mostly good news: content updates reach installed phones with zero rebuilds and zero store reviews — but on ISR's schedule, not the instant you hit merge. If a stakeholder expects the app to be “live” the second a CMS entry changes, check the route's actual revalidate window or wire up on-demand revalidation before they file it as an app bug.

The real trap: OAuth pops out of the WebView

Google's “Use secure browsers” policy has hard-blocked its OAuth authorization endpoint inside embedded WebViews for years — users see 403: disallowed_useragent instead of an account picker. This applies to every WebView wrapper on the market; any vendor implying otherwise is describing a policy violation. So “Sign in with Google” must leave the shell and complete in the system browser or a custom tab.

The subtle part is what happens after: the session that OAuth flow creates lives in the external browser's cookie jar. The app's WebView has its own. Without deliberate handling, the user “logs in,” switches back to your app, and is still anonymous — the single most confusing failure mode a wrapped app can ship with.

What actually works

Keep one first-party login path alive: Auth.js/NextAuth credentials, or an email one-time code the user types inside the app. Cookies set by your own domain behave normally in the WebView, so first-party sessions persist exactly like in a browser. Avoid magic links as the only option — tapping one in Gmail opens the phone's default browser and creates the session there, same trap. If OAuth is currently your only door, test your login end to end on the free first build before you publish anything.

Android, iOS and Windows have different delivery requirements

Android is the fast path: a signed APK for direct testing and an AAB for Google Play, built with a per-project keystore, in a few minutes. You'll need an app icon of at least 512×512 (PNG) and, for the store, Google's $25 one-time developer account. Push via OneSignal is wired into the shell — for a SaaS or content product it's the one genuinely native capability that justifies the app over a bookmark. The generic walkthrough of the free build and Play submission is in our free-Android-build guide.

iOS deserves plain language, because this is a separate delivery path. Eligible App, Builder and Pro customers can request an IPA compiled on Apple hardware; output is unsigned by default. Optional cloud signing requires active Pro and compatible certificate/private-key and provisioning profile inputs. A .p12 alone is not enough. Check the bundle ID, signing identity and export method in the actual result, following iOS setup. Submission and approval are separate; a login screen or native shell does not guarantee compliance with Apple's Guideline 4.2. A Web Clip is a different home-screen shortcut, not a signed IPA.

Windows is a different product surface entirely: eligible App or Pro access can wrap the deployed URL into a Windows EXE. Builder alone does not include Windows. Check the current offer and the output's distribution/signing details. That path has its own guide — converting Next.js to a Windows EXE — and shares nothing with the mobile pipeline except the URL you paste.

If what you have is source code, not a URL

Next.js shops usually have other codebases too. Code2Native's other product surface compiles uploaded source — Flutter, native Android (Gradle), and Capacitor projects that include an android folder — into signed binaries with era-matched toolchains. React Native, Ionic, and Expo source builds are currently waitlist-only, not live. Next.js itself isn't a source-build target — its whole value is server-side, which is exactly why the wrapper path above is the Next.js path.

What it costs

One URL-based Android Preview APK is free, with the Code2Native mark on its bottom strip. This is not three free builds per day. Paid URL-wrap rebuilds cost 0 credits. Source compiles require Builder or Pro and spend bundled credits; Builder allows 20 Android source builds per calendar month. IPA and Windows have separate eligibility and charges. Platform/network faults refund credits actually charged; customer compile errors do not. Check the pricing page.

FAQ

Can I convert a Next.js app to a mobile app without rewriting it?

Yes, if a wrapper fits your product. Your deployed Next.js app is a running server that any Chromium browser can render, and an Android WebView is one of those browsers. We wrap your production URL in a native shell and return a signed APK/AAB in minutes, with OneSignal push. It's your live site in a shell — not a native rewrite — so server components, middleware, ISR, and API routes keep running on your server. If you need offline mode or heavy native UI, that's rewrite territory — a different project entirely.

Does SSR and the App Router work inside the wrapped app?

Yes. The Android WebView is Chromium-based, so server-rendered HTML, React Server Components, streaming, and client-side transitions via next/link behave as they do in mobile Chrome. Both routers push real History API entries, so the Android back button steps backward through in-app navigation instead of closing the app. Middleware (proxy.ts in Next.js 16), ISR, and API routes are untouched — they run on your server, not the device.

Why does Google sign-in fail inside a WebView app?

Google's “Use secure browsers” OAuth policy blocks its authorization endpoint in embedded WebViews — users get a 403 disallowed_useragent error. The flow has to hop to the system browser, and a session created out there doesn't automatically exist in the app's WebView. The reliable fix is one first-party login path — credentials or an email code typed inside the app — since your own domain's cookies work normally in the WebView. If OAuth is your only login, test the free first build before publishing.

Do I get an iOS app too?

Eligible App, Builder and Pro customers can request an unsigned IPA from the website URL. Optional cloud signing requires active Pro plus matching certificate/ private-key and provisioning profile inputs. Check the export method; an IPA entitlement is not signing access or approval. A Web Clip is a separate home-screen shortcut. Submission stays with you, and we do not request your Apple ID password.

Does the app update when I redeploy on Vercel?

It loads your live deployment subject to server and browser caching, including ISR. Native-shell changes need rebuilding and store policies still apply to web-delivered changes. Keep the package ID stable for an existing app; changing it creates a different app rather than an update.

What does this cost for a Next.js app?

One marked URL-based Preview APK is free. Paid Android wraps cost 0 credits; Builder/Pro source compiles spend bundled credits, with 20 Android source builds per calendar month on Builder. IPA and Windows have separate eligibility and charges. Platform/network faults refund actual charges; customer compile errors do not. Store accounts are separate. See current pricing.

Point the wrapper at your deployment and judge it on a real phone

The first Android build is free: paste your production URL, and in a few minutes you'll have a signed APK on a device — server components, middleware, ISR and all. Test your login flow while you're at it; the build will tell you more than this post can.

C

Code2Native Engineering

Engineering team

Written by the Code2Native engineering team — the people who build and operate the cloud build pipeline.