Skip to content

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.

app/routes/home.tsx
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.

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 also carries geo (typed visitor geo — there is no request.cf in production) and the session. See Local development for the runtime constraints.

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.