Lovable TanStack Start app to Android and iOS (2026)
dist/ finds no index.html there. This guide shows how Corenna builds these apps for Android and iOS, and the one thing to set up if your app uses server functions.Which kind of Lovable app do you have?
- TanStack Start (projects created since May 13, 2026):
package.jsonlists@tanstack/react-startand Lovable's@lovable.dev/vite-tanstack-config, pages live insrc/routes/, and there is noindex.htmlin the repo. - Vite and React (older projects): an
index.htmlat the top of the repo and a build that writes the site todist/. These keep working exactly as before; the Lovable, Bolt or v0 to APK guide covers them.
Not sure? Paste the repo into the free repo check. It names the framework, and for a TanStack Start app it says whether the app needs a hosted backend.
What Corenna does with a TanStack Start app
Your website's own build stays as it is. For the app, Corenna makes a second, client-only build in TanStack Start's SPA mode, from a copy of your Vite config made during the build, so nothing in your repo changes. It packages only the files a browser runs, never server code or .env files, and the app opens on the start page that build writes. Capacitor is added at build time as well, so the repo needs no mobile setup of its own.
If your package.json has a build:mobile script, Corenna runs that instead and packages what it produces.
Does your app need a server?
It does if it uses server functions: code wrapped in createServerFn, usually in files named *.functions.ts. Those run on your website's server, and an installed app has none.
- No server functions: nothing to set up. The app runs from its own files, and calls to Supabase or any other service go straight from the phone, as they do on the website.
- Server functions: the installed app sends them to your published website. Set that up once, as below.
If your app uses server functions: set a hosted backend
- Publish the app in Lovable. The installed app calls the code that is published, so publish again whenever you change a server function: one the site doesn't have yet fails in the app.
- Set the Hosted backend URL on your Corenna project page to the published address, such as
https://your-app.lovable.appor your own domain: https, with no path after it. - Let the website accept the app's requests. The installed app calls from its own origin,
https://localhoston Android andcapacitor://localhoston iOS. Browsers need CORS headers for that, and TanStack Start's CSRF protection turns away other origins by default. Thissrc/start.tslets both through:
Lovable's ownimport { createCsrfMiddleware, createMiddleware, createStart } from "@tanstack/react-start"; // The installed app calls server functions from its own origin: // https://localhost on Android, capacitor://localhost on iOS. const APP_ORIGINS = ["https://localhost", "capacitor://localhost"]; const corsHeaders = (origin: string) => ({ "Access-Control-Allow-Origin": origin, "Access-Control-Allow-Methods": "GET, POST, OPTIONS", "Access-Control-Allow-Headers": "content-type, accept, authorization, x-tsr-serverfn", // TanStack Start reads these on every server function response. "Access-Control-Expose-Headers": "x-tss-serialized, x-tss-raw, x-tss-context", Vary: "Origin", }); const appCors = createMiddleware({ type: "request" }).server(async ({ request, next }) => { const origin = request.headers.get("Origin"); const fromApp = origin !== null && APP_ORIGINS.includes(origin); if (fromApp && request.method === "OPTIONS") { return new Response(null, { status: 204, headers: corsHeaders(origin) }); } const result = await next(); if (fromApp) { for (const [k, v] of Object.entries(corsHeaders(origin))) result.response.headers.set(k, v); } return result; }); const csrf = createCsrfMiddleware({ filter: (ctx) => ctx.handlerType === "serverFn", secFetchSite: (site, ctx) => site === "same-origin" || APP_ORIGINS.includes(ctx.request.headers.get("Origin") ?? ""), origin: (origin, ctx) => origin === new URL(ctx.request.url).origin || APP_ORIGINS.includes(origin), }); export const startInstance = createStart(() => ({ requestMiddleware: [appCors, csrf], }));src/start.tsalready has an error handler and a CSRF middleware: keep the error handler, and use these two in place of its CSRF middleware. Or ask Lovable to do it:
Then publish again.In src/start.ts, let my mobile app call server functions. Accept requests from the origins https://localhost and capacitor://localhost with CORS: answer OPTIONS requests, allow the content-type, accept, authorization and x-tsr-serverfn headers, and expose x-tss-serialized, x-tss-raw and x-tss-context. Make the CSRF middleware accept those two origins for server function calls. Keep the rest of the file as it is. - Build. Before compiling, Corenna calls your website the way the app will. If the site doesn't answer, redirects, or turns the app away, the build stops there and says what to change, so a broken app never gets built.
Signed-in calls from the app carry the user's token in a header (see below), so this opens your server functions to the app without loosening the website's own protection.
What stops a build, and why
Corenna checks for these before anything is compiled, and names the file it found:
- Server functions with no hosted backend. Set the Hosted backend URL as above.
- Calls to your own server by a relative URL, like
fetch("/api/..."). In the app, a relative URL points at the phone, not your website. Use the site's full https:// address, or move the call into a server function. - Server code that reads a cookie session. The app calls your website from its own origin, where the site's cookies are never sent, so the server would see a signed-out user. Send the user's access token in an
Authorizationheader instead. With Supabase, that is the session'saccess_token, checked on the server withauth.getUser(token). - A
server.urlincapacitor.config. It makes the app load a website instead of the app Corenna builds. It is for live reload during development; remove it. - Old versions. The mobile build needs
@tanstack/react-start1.121.0 or later, and sending server functions to a hosted backend needs@tanstack/start-client-core1.153.0 or later. New Lovable projects are well past both.
Server routes, such as API endpoints for webhooks, are fine: they stay on your website, and the build just notes that the app must reach them by the site's full address.
Build it
- Connect the Lovable project to GitHub: add a GitHub connection under Workspace settings → Git → GitHub, then click Connect in the project's GitHub settings.
- Sign in to Corenna with GitHub, give the Corenna GitHub App access to the repo, and create the project. Set the Hosted backend URL if the app needs one.
- Start an Android build: an
.apkto install on a phone, or an.aabfor Google Play. Your first successful Android build is free, with no card. - For iPhone, you need an Apple Developer account ($99/year, paid to Apple) and its certificate and provisioning profile. The build runs on GitHub's macOS runners, so no Mac is needed.
Good to know
- The app's pages, scripts and images are inside the app, so it opens without a connection; anything that needs a server or another website needs one. That includes fonts loaded from Google Fonts, which fall back to the phone's own fonts when offline.
- To update the app: change it in Lovable, publish if a server function changed, and build again. The build number goes up after every successful build, so each new version installs over the last.