Files
Rami BitarandClaude Opus 5.5 55423c7204 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 <noreply@anthropic.com>
2026-10-03 15:47:53 -04:00

4.3 KiB

Vite Shopify Storefront

A server-rendered Shopify storefront built with Vite, TanStack Start (TanStack Router + TanStack Query), Hono and Tailwind CSS v4, talking to Shopify's Storefront API directly over GraphQL — no Shopify SDK. It's a port of nextjs-templates/shopify-storefront without the AI store assistant.

Runs in the nodesandbox runtime: npm install → npm run dev → port 3000.

Getting started

cp .env.example .env.local   # optional; without it the store is mock.shop
corepack enable              # uses the Yarn 4 pinned in package.json
yarn install
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; 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 its installer resolves from package.json ranges rather than yarn.lock. So:

  • vite is pinned to 8.3.2, which uses rolldown 1.2.x. The runtime must support rolldown 1.2's WASI binding (@emnapi/* 2.0 / @napi-rs/wasm-runtime 1.2); runtimes that only support rolldown 1.1.x fail to boot with "WASI binding not found". Vite 8.1.3 is the last release on rolldown 1.1.x.
  • WASI bindings (@rolldown/binding-wasm32-wasi, @tailwindcss/oxide-wasm32-wasi) are not listed here: the runtime installs each package's *-wasm32-wasi 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.
  • Nitro (the hosting adapter) only loads for vite build, never in vite dev.

Layout

src/
  server.ts        Start's server entry: Hono routes /api/*, the rest goes to Start
  router.tsx       Router + per-request QueryClient, SSR query integration
  routes/          File routes; loaders prefetch into TanStack Query
  server/
    app.ts         Hono API
    account.ts     login, logout, register, recover, reset, activate, me
    account.functions.ts  server functions for account reads in loaders
  hooks/           TanStack Query options + hooks over the Storefront API
  services/shopify Storefront API client (plain fetch), catalogue and customer functions
  graphql/         Storefront API queries and mutations (`gql()` joins fragments)
  components/      Storefront UI (header, cart drawer, product detail, ...)

How the Next.js pieces map

Next.js Here
app/**/page.tsx src/routes/** (TanStack file routes)
generateMetadata / metadata route head() via src/lib/seo.ts, server-rendered
Server components fetching data route loaders seeding TanStack Query, dehydrated to the client
app/api/account/* route handlers src/server/account.ts (Hono)
next/headers cookies hono/cookie, and getCookie in server functions
next/image src/components/ui/image.tsx (Shopify CDN srcset)
NEXT_PUBLIC_* env VITE_* env (VITE_SHOPIFY_DOMAIN defaults to mock.shop)

Cart and catalogue reads after the first page load go straight from the browser to the Storefront API with the public token. Account requests stay on the server, so the customer access token never reaches client JavaScript.