Development example for exercising Hydrogen APIs in SolidStart. This is not a starter template. See
../README.mdfor context.
Port of the canonical examples/core design to SolidStart v1 (Vinxi + Solid Router, SSR enabled by default).
- Signals as a first-class citizen.
createSignaldrives cart, product form, and collection browse state derived from Hydrogen's core stores — no virtual DOM, nouseState, no batching. The reactive primitive is the model. query+createAsyncas the data path. Each route defines a server-sidequery, exports aroute.preloadso the router can kick the fetch off during navigation, and reads the result withcreateAsync()— Solid's idiomatic SSR-aware fetcher."use server"on every fetcher. Storefront API calls run on the server during SSR and on subsequent client navigations are issued as RPC, not from the browser.- Core Hydrogen primitives for cart, product variant selection, collection filtering/sorting, and Shop Pay without framework bindings.
- File-based routing via
FileRoutes.src/routes/is the source of truth — folder layout maps directly to URL paths, including[handle].tsxdynamic params. - Shared chrome (
Header,Footer) at the root. Mounted insrc/app.tsx'sRouterroot, not duplicated per-route. - Tailwind v4 via
@tailwindcss/vite. The design's@themetokens live insrc/app.css.
| Route | Source | Status |
|---|---|---|
/ |
examples/core/reference/home.html |
Live: hero is static; featured products grid pulls from products(first: 3) |
/collections |
examples/core/reference/collections.html |
Live: queries collections(first: 12) |
/collections/:handle |
examples/core/reference/collection.html |
Live: queries collection(handle:) with filters, sort, and active filter chips |
/products/:handle |
examples/core/reference/product.html |
Live: gallery, URL-synced variant options, add-to-cart, Shop Pay, and related products |
/cart |
(Hydrogen primitive) |
Live: optimistic line updates, discount codes, order note, totals, checkout, and Shop Pay |
/blogs/news |
(new) | Live: featured article + grid from blog(handle:"news").articles |
/blogs/news/:handle |
(new) | Live: title, date, contentHtml rendered via innerHTML |
/account |
(new) | Live: Customer Account OAuth session, basic customer name/email, logout form |
/account/login |
(new) | Live: Hydrogen Customer Account handler starts OAuth and stores pending PKCE state |
POST /account/logout |
(new) | Live: Hydrogen Customer Account handler clears session and redirects with 303 See Other |
- Live: product, collection, blog, and article data; prices; images; URL-synced options; cart mutations; collection filters/sort; checkout links; Shop Pay buttons; Customer Account login/session state, basic customer identity, logout.
- Stubbed: hero links, search, newsletter form.
The account flow uses createCustomerSession and createCustomerAccountServerHandlers from @shopify/hydrogen/customer-account, Customer Account values from examples/shared/config.ts, and an encrypted HttpOnly __Host- cookie adapter from examples/shared/customer-session.ts.
Customer Account OAuth requires a public HTTPS origin. To test locally without a tunnel, run:
pnpm --filter @shopify/hydrogen-example-solid-start dev:httpsOn the first run, restart the command after the Vite plugin provisions the certificate so Vinxi can load it.
When the server starts, the local HTTPS plugin links an unlinked Hydrogen storefront and pushes the Customer Account callback, JavaScript origin, and logout URLs. If automatic setup is unavailable, it prints the values for manual configuration.
# from the repo root
pnpm install
pnpm --filter @shopify/hydrogen-example-solid-start devThen open http://localhost:3000.
Vinxi has no config field for its bind target; it reads HOST (and PORT) from the environment when the listener starts. Shopify-managed dev machines export HOST globally — without an override the dev server tries to bind to that hostname, falls back through alternative ports, and prints an unreachable network URL.
app.config.ts sets process.env.HOST/PORT after evaluating the local HTTPS plugin: local.tryhydrogen.dev:5173 for dev:https, localhost otherwise. Doing it in the config rather than as a HOST=... prefix in the scripts keeps dev and dev:https working on Windows shells, which treat HOST=x as a command. start still inlines HOST=localhost because vinxi start serves the built output and never loads app.config.ts.
These are calls/patterns that would benefit from a @shopify/hydrogen helper as the core package grows:
storefrontClient.graphql()fromgetRequestStorefrontClient()— now uses the request-scoped client created insrc/middleware.tswithgql.tadafor zero-config type inference. Error normalization and request-id propagation are handled by the core client.formatMoney()insrc/lib/money.ts— every example will need money formatting from aMoneyV2-shaped object.ProductCardreads a narrow product shape (handle,title,featuredImage,priceRange.minVariantPrice). A core fragment + type for "product card" would let routes share GraphQL fragments instead of re-listing fields.- Option value swatch data is exposed through
createProductFormStore().getState().options[].values[].swatch, so selectors can render API-backed swatches withvalue.swatch.
- Framework bindings. This example intentionally uses core primitives directly. If Solid bindings are added later, compare them against
src/lib/cart.tsx,src/components/CollectionBrowser.tsx, andsrc/components/ProductPurchasePanel.tsx. starton Windows.vinxi startbypassesapp.config.ts, so itsHOST=localhostprefix is still shell-specific. A cross-platform fix would need a small Node launcher or across-env-style helper; not worth a dependency for a dev-only example yet.