Routes & data
A theme’s pages are ordinary React Router 7 routes, registered in app/routes.ts. Data comes
from the Storefront API through context.storefront — the SDK client the Worker entry puts
on every request’s load context.
Loaders
Section titled “Loaders”import { PRODUCTS_QUERY, type ProductsQueryData } from "../graphql/queries";import type { Route } from "./+types/home";
export async function loader({ context }: Route.LoaderArgs) { return context.storefront.query<ProductsQueryData>(PRODUCTS_QUERY, { variables: { first: 12 }, });}
export default function Home({ loaderData }: Route.ComponentProps) { const { shop, products } = loaderData; // …}Route.LoaderArgs / Route.ComponentProps come from React Router’s typegen
(bun run typecheck runs it). Queries live as #graphql documents in app/graphql/ —
run bun run codegen to generate result types from your shop’s schema.
Adding a route
Section titled “Adding a route”Register it in app/routes.ts, create the module under app/routes/, and export a loader
plus a default component. The scaffold’s $.tsx catch-all is your 404 — keep it last.
Context beyond data
Section titled “Context beyond data”context also carries geo (typed visitor geo — there is no request.cf in production) and
the session. See Local development for the runtime
constraints.
Caching
Section titled “Caching”Wrap expensive lookups with the cache helpers from @kotao/storefront: CacheShort,
CacheLong, CacheNone, CacheCustom and createWithCache control the cache strategy per
query; generateCacheControlHeader translates a strategy into response headers.