Template
Vercel's 'Hydrogen (v1)' preset matches any project depending on
@shopify/hydrogen and runs its v1 builder, which fails looking for
dist/worker. We only used the preview package as a Storefront API client,
so talk to the API directly instead:
- services/shopify/client.ts: plain fetch to /api/{version}/graphql.json
with the public token header, @inContext country/language defaults, and
the same graphql() / unwrapStorefrontResult API as before
- graphql/gql.ts: small helper that joins a document with its fragments
(de-duplicated); the queries themselves are unchanged
- Explicit response types at each call site in place of the generated
gql.tada types; local ProductFilter input type
- Drop @shopify/hydrogen, graphql, the TS plugin and 'hydrogen gql check'
- Copy no longer mentions Hydrogen
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3.7 KiB
3.7 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 to dist/ |
yarn typecheck |
tsc |
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:
viteis pinned to 8.1.3, which uses rolldown 1.1.x, androlldownto 1.1.4. Rolldown 1.2.x's WASI binding needs glue the runtime doesn't support yet ("WASI binding not found"). Bump these only together with the runtime.- WASI bindings (
@rolldown/binding-wasm32-wasi,@tailwindcss/oxide-wasm32-wasi) are not listed here: the runtime installs each package's*-wasm32-wasioptional 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: it brings its own rolldown 1.2.x.
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.