CoursesSanity and Shopify with HydrogenFetching Sanity content
Sanity and Shopify with Hydrogen

Fetching Sanity content

Your front-end is only telling half the story. Bring your products to life with extra content from your Sanity project.
Log in to mark your progress for each Lesson and Task

The hydrogen-sanity package contains a number of useful functions to simplify querying and displaying content from Sanity.

Run the following command to install hydrogen-sanity and other packages
# in the /web directory
npm install hydrogen-sanity @sanity/client @portabletext/react
  • hydrogen-sanity is a multi-purpose toolkit for integrating Sanity with Hydrogen
  • @sanity/client is the primary way for JavaScript applications to interact with Sanity content and API's
  • @portabletext/react is a React component for rendering block content which is authored in Sanity Studio

You may encounter a Vite error in the next few steps, to get ahead of that we'll add the following to our vite.config.ts file:

import {defineConfig} from 'vite'
import {hydrogen} from '@shopify/hydrogen/vite'
import {sanity} from 'hydrogen-sanity/vite'
export default defineConfig({
plugins: [hydrogen(), sanity() /** ... */],
// ... other config
})

The Sanity Client allows you to query (and mutate) content.

You’ll need a Sanity Client configured with your Project ID and dataset name available to any route.

Add your Sanity project details to the Hydrogen project's .env file. You can find these either in sanity.io/manage or in the sanity.config.ts file of your Studio.

Update the web/.env file to include Sanity project details.
# Project ID
SANITY_PROJECT_ID=""
# Dataset name
SANITY_DATASET=""
# (Optional) Sanity API version
SANITY_API_VERSION=""
Update the TypeScript configuration by adding these variables to Remix's global types.
// ...all other type settings
declare global {
interface Env extends HydrogenEnv {
SANITY_PROJECT_ID: string;
SANITY_DATASET: string;
SANITY_API_VERSION: string;
SANITY_API_TOKEN: string;
}
}

The code example below contains new lines to paste into your Hydrogen project’s context file, this was previously handled in server.ts.

Update web/lib/context.ts and update it to include Sanity Client
import {createHydrogenContext} from '@shopify/hydrogen';
import {createSanityContext, type SanityContext} from 'hydrogen-sanity';
import {AppSession} from '~/lib/session';
import {CART_QUERY_FRAGMENT} from '~/lib/fragments';
// ...other types and imports
declare global {
interface HydrogenAdditionalContext extends AdditionalContextType {
// Augment `HydrogenAdditionalContext` with the Sanity context
sanity: SanityContext;
}
}
/**
* Creates Hydrogen context for React Router 7.9.x
* Returns HydrogenRouterContextProvider with hybrid access patterns
* */
export async function createHydrogenRouterContext(
request: Request,
env: Env,
executionContext: ExecutionContext,
) {
// ...other functions
// 1. Add `sanity` configuration
const sanity = await createSanityContext({
request,
// To use the Hydrogen cache for queries
cache,
waitUntil,
// Sanity client configuration
client: {
projectId: env.SANITY_PROJECT_ID,
dataset: env.SANITY_DATASET || 'production',
apiVersion: env.SANITY_API_VERSION || 'v2025-11-01',
useCdn: process.env.NODE_ENV === 'production',
},
});
// 2. Make `sanity` available to loaders and actions in the request context
const hydrogenContext = createHydrogenContext(
{
env,
request,
cache,
waitUntil,
session,
// Or detect from URL path based on locale subpath, cookies, or any other strategy
i18n: {language: 'EN', country: 'US'},
cart: {
queryFragment: CART_QUERY_FRAGMENT,
},
},
{...additionalContext, sanity},
);
return hydrogenContext;
}

Now a configured Sanity React Loader is available inside the Hydrogen “context” as sanity, just like the preconfigured storefront.

You'll use loadQuery to retrieve content from Sanity. It's configured in a way to make setting up Visual Editing simple. For now, you'll just use the initial data in the default export to render content.

Update the route for all product pages to query for Sanity content
import {Link, useLoaderData, type LoaderFunctionArgs} from 'react-router';
import {type Product} from '@shopify/hydrogen/storefront-api-types';
import {PortableText, type PortableTextBlock} from '@portabletext/react';
export async function loader({
params,
context: {storefront, sanity},
}: LoaderFunctionArgs) {
const {product} = await storefront.query<{product: Product}>(
`#graphql
query Product($handle: String!) {
product(handle: $handle) { id title }
}
`,
{variables: params},
);
const PRODUCT_QUERY = `*[_type == "product" && store.slug.current == $handle][0]{
body,
"image": store.previewImageUrl
}`;
const initial = await sanity.fetch<{
body: PortableTextBlock[] | null;
image: string | null;
} | null>(PRODUCT_QUERY, params, {
tag: 'homepage',
hydrogen: {debug: {displayName: 'query Homepage'}},
});
if (!initial) {
throw new Response('Product not found', {status: 404});
}
return {product, initial};
}
export default function Product() {
const {product, initial} = useLoaderData<typeof loader>();
return (
<div className="mx-auto p-12 grid grid-cols-1 gap-4">
<h1 className="text-3xl font-bold">{product.title}</h1>
{initial?.image ? (
<img
alt={product.title}
src={initial.image}
className="size-32 mb-6 mr-6 object-cover float-left rounded-xl"
/>
) : null}
{Array.isArray(initial?.body) ? (
<PortableText value={initial.body} />
) : null}
<Link className="text-blue-500" to="/collections/all">
&larr; Back to All Products
</Link>
</div>
);
}
Read the documentation on GROQ for more on querying content from Sanity

Visit any product page now, and you should see both the product title from Shopify and the contents of the Portable Text field in the Sanity document for that same product.

If there's no extra text showing, edit the LULU Mini Pot product in your Studio and add the following text:

All of our products are made from 100% recycled materials and are finished with a waterproof sealant. We recommend cleaning your LULU Pot regularly with hot soapy water. Please do not leave the pot to soak. Alternatively our products are dishwasher safe.

Your page should now look like this.

If your CSS looks slightly off, try emptying the contents of reset.css as it conflicts with some Tailwind Typography styles.

Your Hydrogen app now queries content from both Sanity and Shopify independently, displaying in a consistent front-end.

Sanity content is more than just a paragraph of text – let’s embellish this with some rich content blocks!

You have 5 uncompleted tasks in this lesson
0 of 5