From 55423c720401668db5773b6ba7e0a7019f15d5e0 Mon Sep 17 00:00:00 2001 From: Rami Bitar Date: Sat, 3 Oct 2026 15:47:53 -0400 Subject: [PATCH] Deploy to Vercel as a TanStack Start app via Nitro Vercel served 404s: with Hydrogen gone it treated the app as a static Vite site, but TanStack Start's build has no index.html; pages come from its server. Add the hosting adapter TanStack documents for Vercel: - nitro() in vite.config, for 'vite build' only (dev and the nodesandbox runtime never load it); on Vercel it writes .vercel/output with the static assets and one Node.js function running Start + Hono - vercel.json: tanstack-start preset, Yarn 4 via Corepack - 'yarn start' runs the Node build; canonical URLs use the Vercel domain Co-Authored-By: Claude Opus 5.5 --- .gitignore | 3 + README.md | 14 ++++- package.json | 2 + vercel.json | 6 ++ vite.config.mjs | 18 ++++-- yarn.lock | 146 +++++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 vercel.json diff --git a/.gitignore b/.gitignore index 7c118a3..3afb834 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ .DS_Store node_modules/* dist +.output +.nitro +.vercel .yarn/install-state.gz .env*.local src/routeTree.gen.ts diff --git a/README.md b/README.md index 922e610..9680031 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,19 @@ yarn dev # http://localhost:3000 | Script | What it does | | ---------------- | --------------------------------------------------------- | | `yarn dev` | Vite dev server with SSR and the Hono `/api` routes | -| `yarn build` | Production build to `dist/` | +| `yarn build` | Production build; Nitro packages the server for the host | +| `yarn start` | Run the Node build from `.output/` | | `yarn typecheck` | `tsc` | +## Deploying to Vercel + +Push and deploy — no dashboard settings. `vercel.json` selects the TanStack +Start preset and installs with Yarn 4 via Corepack. During `vite build` the +Nitro plugin detects Vercel and writes `.vercel/output/` (static assets plus one +Node.js function running TanStack Start, with Hono serving `/api/*`). Set +`VITE_SHOPIFY_*` in the project to use a real store; without them it uses +mock.shop. + ## nodesandbox compatibility The sandbox runs Vite's native tools as WebAssembly (`NAPI_RS_FORCE_WASI`), and @@ -37,7 +47,7 @@ its installer resolves from `package.json` ranges rather than `yarn.lock`. So: optional dependency itself, and npm on other hosts rejects them (`EBADPLATFORM`). - `@vitejs/plugin-react` (6.0.0) and Tailwind (4.3.1) match the harness samples. -- No Nitro. +- Nitro (the hosting adapter) only loads for `vite build`, never in `vite dev`. ## Layout diff --git a/package.json b/package.json index 2c42feb..c8f8400 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "dev": "vite dev --host 0.0.0.0 --port 3000", "build": "vite build", + "start": "node .output/server/index.mjs", "typecheck": "tsc --noEmit" }, "packageManager": "yarn@4.18.0+sha512.fcb8716fe7cd0eece141ffc18b92193a9df9204c1ba83189c288835223fc0bbe64af473bab0d5e9927a7daeb5caf2bb07eb2787cc9338ca040ea125f2a1f2f7e", @@ -24,6 +25,7 @@ "framer-motion": "^12.42.2", "hono": "^4.13.12", "lucide-react": "^0.562.0", + "nitro": "3.0.260903-beta", "radix-ui": "^1.6.7", "react": "^19.3.0", "react-dom": "^19.3.0", diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..4ec2022 --- /dev/null +++ b/vercel.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "framework": "tanstack-start", + "installCommand": "corepack enable && yarn install", + "buildCommand": "yarn build" +} diff --git a/vite.config.mjs b/vite.config.mjs index 72fa8e6..95fa4de 100644 --- a/vite.config.mjs +++ b/vite.config.mjs @@ -5,14 +5,24 @@ import tailwindcss from '@tailwindcss/vite'; import { fileURLToPath, URL } from 'node:url'; // Absolute origin for canonical and Open Graph URLs. Inlined at build time so -// the server render and the client agree on it; set VITE_SITE_URL in production. -const siteUrl = process.env.VITE_SITE_URL ?? 'http://localhost:3000'; +// the server render and the client agree on it. On Vercel the production domain +// is used automatically; VITE_SITE_URL overrides it anywhere. +const siteUrl = + process.env.VITE_SITE_URL ?? + (process.env.VERCEL_PROJECT_PRODUCTION_URL + ? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}` + : 'http://localhost:3000'); -export default defineConfig({ +export default defineConfig(async ({ command }) => ({ server: { port: 3000 }, plugins: [ // Start must come before the React plugin. tanstackStart(), + // Hosting adapter for `vite build`: Nitro packages Start's server for the + // host (a Vercel Function when building on Vercel, a Node server in + // .output/ elsewhere). Dev never loads it, so `vite dev` — and the + // nodesandbox runtime — run plain Vite + TanStack Start + Hono. + ...(command === 'build' ? [(await import('nitro/vite')).nitro()] : []), react(), tailwindcss(), ], @@ -24,4 +34,4 @@ export default defineConfig({ '@': fileURLToPath(new URL('./src', import.meta.url)), }, }, -}); +})); diff --git a/yarn.lock b/yarn.lock index d0d182c..5ce593d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2465,6 +2465,13 @@ __metadata: languageName: node linkType: hard +"consola@npm:^3.4.2": + version: 3.4.2 + resolution: "consola@npm:3.4.2" + checksum: 10c0/7cebe57ecf646ba74b300bcce23bff43034ed6fbec9f7e39c27cee1dc00df8a21cd336b466ad32e304ea70fba04ec9e890c200270de9a526ce021ba8a7e4c11a + languageName: node + linkType: hard + "convert-source-map@npm:^2.0.0": version: 2.0.0 resolution: "convert-source-map@npm:2.0.0" @@ -2479,6 +2486,18 @@ __metadata: languageName: node linkType: hard +"crossws@npm:^0.4.12": + version: 0.4.12 + resolution: "crossws@npm:0.4.12" + peerDependencies: + srvx: ">=0.11.5" + peerDependenciesMeta: + srvx: + optional: true + checksum: 10c0/28d95429d666e2b925ccdc1c2f01ad23de21b7a8772cf063281127b9eea4a1b80c459f8404957d7231609750ff81ed6a6514e9c393a3130f35919e7a98a854ce + languageName: node + linkType: hard + "csstype@npm:^3.2.2": version: 3.2.3 resolution: "csstype@npm:3.2.3" @@ -2486,6 +2505,13 @@ __metadata: languageName: node linkType: hard +"db0@npm:^0.4.1": + version: 0.4.1 + resolution: "db0@npm:0.4.1" + checksum: 10c0/2f965e59496455e2c8105f7f17bbbb8a93aefbe6d023cb6670ccba4757ee3ea99ae45be9b190ce87a98257d75a11fa98d1fbe506757360f7dc57adb9465c953e + languageName: node + linkType: hard + "debug@npm:^4.1.0, debug@npm:^4.3.1": version: 4.4.3 resolution: "debug@npm:4.4.3" @@ -2543,6 +2569,20 @@ __metadata: languageName: node linkType: hard +"env-runner@npm:^0.2.1": + version: 0.2.3 + resolution: "env-runner@npm:0.2.3" + dependencies: + crossws: "npm:^0.4.12" + exsolve: "npm:^1.1.1" + httpxy: "npm:^0.5.5" + srvx: "npm:^1.0.5" + bin: + env-runner: ./dist/cli.mjs + checksum: 10c0/b72e0cf4dc17257b963c4dd265749bf68eea721558d82bcedb7b0684d946c8624b8c57949485f54bef86fea92a3fe5abce66cd1e3b70abcb8177950079fb5f0d + languageName: node + linkType: hard + "escalade@npm:^3.2.0": version: 3.2.0 resolution: "escalade@npm:3.2.0" @@ -2662,6 +2702,26 @@ __metadata: languageName: node linkType: hard +"h3@npm:^2.0.1-rc.31": + version: 2.0.1-rc.32 + resolution: "h3@npm:2.0.1-rc.32" + dependencies: + rou3: "npm:^0.9.2" + srvx: "npm:^1.0.5" + peerDependencies: + crossws: ^0.4.12 + ocache: ">=0.3.0" + peerDependenciesMeta: + crossws: + optional: true + ocache: + optional: true + bin: + h3: ./bin/h3.mjs + checksum: 10c0/182881ebf9c866f79ca120e8bc1b8a8850c34673758b67593fed7217cf04387294241e63397e20dd690c39cad05c6ce070eefc0707d7813ecfa9e358c66edd59 + languageName: node + linkType: hard + "hono@npm:^4.13.12": version: 4.13.12 resolution: "hono@npm:4.13.12" @@ -2669,6 +2729,20 @@ __metadata: languageName: node linkType: hard +"hookable@npm:^6.1.1": + version: 6.1.2 + resolution: "hookable@npm:6.1.2" + checksum: 10c0/ea5cdb1b1a1919b48cfbbfb55c8009f6843473ff50f67d6e959fd291cb2fd0a4934e85124bfd6531b1d2edf1e0675bc5954f85a12318a59b15c7a3e5667f0d4e + languageName: node + linkType: hard + +"httpxy@npm:^0.5.5": + version: 0.5.5 + resolution: "httpxy@npm:0.5.5" + checksum: 10c0/7be52cbfbd4157b4ac3aa22124d06081e8ffc50a185622e43c0ce8229bbcd7411d016f4a364fecb694325ed312823efbf51c70fa8049563a8b6b617642914cd3 + languageName: node + linkType: hard + "isbot@npm:^5.1.22": version: 5.2.2 resolution: "isbot@npm:5.2.2" @@ -3043,6 +3117,36 @@ __metadata: languageName: node linkType: hard +"nf3@npm:^0.3.24": + version: 0.3.24 + resolution: "nf3@npm:0.3.24" + checksum: 10c0/6c971b63634b6f7838194b5266fb40a8cb763d55a83b07a58ec3cf044fba1eef2fddfb6ee4d760ac3fbda34f69b9c04ddb923505cff3bbb57b7836c33c54b8ed + languageName: node + linkType: hard + +"nitro@npm:3.0.260903-beta": + version: 3.0.260903-beta + resolution: "nitro@npm:3.0.260903-beta" + dependencies: + consola: "npm:^3.4.2" + crossws: "npm:^0.4.12" + db0: "npm:^0.4.1" + env-runner: "npm:^0.2.1" + h3: "npm:^2.0.1-rc.31" + hookable: "npm:^6.1.1" + nf3: "npm:^0.3.24" + ocache: "npm:^0.3.0" + rolldown: "npm:^1.2.7" + rou3: "npm:^0.9.2" + srvx: "npm:^1.0.3" + unenv: "npm:^2.0.0-rc.24" + unstorage: "npm:^2.0.0-alpha.10" + bin: + nitro: dist/cli/index.mjs + checksum: 10c0/7947c389578c42333b5e8e2bf21e275c5396cb6a9e25ca87ff80c108d199a5a2aa60e08aad369d67c853f866aa4eb355b5e3ca985f17df67dad90f5449a74754 + languageName: node + linkType: hard + "node-gyp@npm:latest": version: 13.1.0 resolution: "node-gyp@npm:13.1.0" @@ -3081,6 +3185,13 @@ __metadata: languageName: node linkType: hard +"ocache@npm:^0.3.0": + version: 0.3.0 + resolution: "ocache@npm:0.3.0" + checksum: 10c0/1caf0f8d24ef638dde0c4a5301376c7c1d0f44f4e7ed5430012c1983d0a1af6436b5243c3adf9c85ec0d4f33c1a2ab211351fe55337cd7308abe190557f37bbd + languageName: node + linkType: hard + "pathe@npm:^2.0.3": version: 2.0.3 resolution: "pathe@npm:2.0.3" @@ -3278,7 +3389,7 @@ __metadata: languageName: node linkType: hard -"rolldown@npm:~1.2.11": +"rolldown@npm:^1.2.7, rolldown@npm:~1.2.11": version: 1.2.12 resolution: "rolldown@npm:1.2.12" dependencies: @@ -3343,6 +3454,13 @@ __metadata: languageName: node linkType: hard +"rou3@npm:^0.9.2": + version: 0.9.2 + resolution: "rou3@npm:0.9.2" + checksum: 10c0/4b0ab14fbd3a7c60ffee1af644f618a9e76dd218cd4fbb585b8fa3601a3f25857c2b1656a215389379a7aef1e1e1897fd55e835658e9c3a5d9683571b36b3bd6 + languageName: node + linkType: hard + "scheduler@npm:^0.28.0": version: 0.28.0 resolution: "scheduler@npm:0.28.0" @@ -3407,6 +3525,15 @@ __metadata: languageName: node linkType: hard +"srvx@npm:^1.0.3, srvx@npm:^1.0.5": + version: 1.0.5 + resolution: "srvx@npm:1.0.5" + bin: + srvx: ./bin/srvx.mjs + checksum: 10c0/7ff87707840af95b399a11ab0b2fba6ded66a1d4658324913a1c95c916304056e5e618a9fc93803c1351aaf6acf4f7f3c5f55f18e82110a2ce876246d770c881 + languageName: node + linkType: hard + "tailwind-merge@npm:^3.7.0": version: 3.7.0 resolution: "tailwind-merge@npm:3.7.0" @@ -3499,6 +3626,15 @@ __metadata: languageName: node linkType: hard +"unenv@npm:^2.0.0-rc.24": + version: 2.0.0-rc.24 + resolution: "unenv@npm:2.0.0-rc.24" + dependencies: + pathe: "npm:^2.0.3" + checksum: 10c0/e8556b4287fcf647f23db790eea2782cc79f182370718680e3aba4753d5fb7177abf5d6df489c8f74f7e3ad6cd554b8623cc01caf3e6f2d5548e69178adb1691 + languageName: node + linkType: hard + "unplugin@npm:^3.3.0": version: 3.4.0 resolution: "unplugin@npm:3.4.0" @@ -3542,6 +3678,13 @@ __metadata: languageName: node linkType: hard +"unstorage@npm:^2.0.0-alpha.10": + version: 2.0.0-alpha.10 + resolution: "unstorage@npm:2.0.0-alpha.10" + checksum: 10c0/60473155ac6900020462f4d4192ff8a300cd510e8739f7b1905faece74fe0438b01cf00a84bcda81166cc3344838492e352998e1c48d8bc1449b1191d82ed3f7 + languageName: node + linkType: hard + "update-browserslist-db@npm:^1.3.3": version: 1.3.3 resolution: "update-browserslist-db@npm:1.3.3" @@ -3619,6 +3762,7 @@ __metadata: framer-motion: "npm:^12.42.2" hono: "npm:^4.13.12" lucide-react: "npm:^0.562.0" + nitro: "npm:3.0.260903-beta" radix-ui: "npm:^1.6.7" react: "npm:^19.3.0" react-dom: "npm:^19.3.0"