📄 SKILL.md

← Vault

name: tanstack-start-vps-supabase-auth

description: Deploy Lovable/TanStack Start app to VPS with Supabase Auth — fix blank screen (SSR doesn't serve static files) and configure Google OAuth

category: devops


TanStack Start + Supabase Auth — VPS Deployment

Context

Deploy a Lovable/TanStack Start app to VPS with Supabase Auth (not Lovable Cloud auth).

Problem

App loads but shows blank screen — TanStack Start node-server target does SSR only, NOT static file serving. Browser can't load JS bundles.

Solution

1. Build (on local/ Lovable)

`

npm run build

`

Produces dist/server/ (SSR handler) and dist/client/ (static JS/CSS).

2. Sync outputs correctly

`bash

SSR handler

cp dist/server/server.js .output/server/index.mjs

cp -r dist/server/assets/* .output/server/assets/

Static assets

cp -r dist/client/assets/* .output/client/assets/

`

Verify manifest hash: grep index-*.js in .output/server/index.mjs and .output/client/assets/.

3. Node HTTP wrapper (server.js)

`javascript

import { fetchHandler } from './.output/server/index.mjs';

import { createServer } from 'http';

import { readFileSync, existsSync } from 'fs';

import { extname, join } from 'path';

const CLIENT_DIR = './.output/client';

const MIME_TYPES = {

'.js': 'application/javascript',

'.css': 'text/css',

'.html': 'text/html',

'.json': 'application/json',

'.png': 'image/png',

'.svg': 'image/svg+xml',

};

const server = createServer((req, res) => {

const filePath = join(CLIENT_DIR, req.url.split('?')[0]);

if (existsSync(filePath) && MIME_TYPES[extname(filePath)]) {

const body = readFileSync(filePath);

res.writeHead(200, { 'Content-Type': MIME_TYPES[extname(filePath)] });

res.end(body);

return;

}

// SSR for everything else

fetchHandler(req, res);

});

server.listen(PORT, () => console.log(Running on :${PORT}));

`

4. Supabase Auth setup