# Fetching Sanity content https://www.sanity.io/learn/course/sanity-and-shopify-with-hydrogen/fetching-sanity-content.md Your front-end is only telling half the story. Bring your products to life with extra content from your Sanity project. ## Install Sanity packages The `hydrogen-sanity` package contains a number of useful functions to simplify querying and displaying content from Sanity. 1. **Run** the following command to install `hydrogen-sanity` and other packages ```sh # in the /web directory pnpm add hydrogen-sanity @sanity/client @portabletext/react ``` * [hydrogen-sanity](https://github.com/sanity-io/hydrogen-sanity) is a multi-purpose toolkit for integrating Sanity with Hydrogen * [@sanity/client](https://github.com/sanity-io/client) is the primary way for JavaScript applications to interact with Sanity content and API's * [@portabletext/react](https://www.npmjs.com/package/@portabletext/react) is a React component for rendering block content which is authored in Sanity Studio ### Update Vite config 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: ```typescript:web/vite.config.ts import {defineConfig} from 'vite' import {hydrogen} from '@shopify/hydrogen/vite' import {sanity} from 'hydrogen-sanity/vite' export default defineConfig({ plugins: [hydrogen(), sanity() /** ... */], // ... other config }) ``` ### Create a client in the React Router server context 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](https://sanity.io/manage) or in the `sanity.config.ts` file of your Studio. 1. **Update** the `web/.env` file to include Sanity project details. ```text:web/.env # Project ID SANITY_PROJECT_ID="" # Dataset name SANITY_DATASET="" # (Optional) Sanity API version SANITY_API_VERSION="" ``` 1. **Update** the TypeScript configuration by adding these variables to Remix's global types. ```typescript:web/env.d.ts // ...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. 1. **Update** `web/lib/context.ts` and update it to include Sanity Client ```typescript:web/app/lib/context.ts 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. 1. **Update** the route for all product pages to query for Sanity content ```tsx:web/app/routes/products.$handle.tsx 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(); return (

{product.title}

{initial?.image ? ( {product.title} ) : null} {Array.isArray(initial?.body) ? ( ) : null} ← Back to All Products
); } ``` 1. Read the [documentation on GROQ](https://www.sanity.io/docs/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](http://localhost:3333/structure/products;shopifyProduct-9024565674264;shopifyProduct-9024565674264) in your Studio and add the following text: ```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. ![Product display page](https://cdn.sanity.io/images/3do82whm/next/f124d9d1f445fd2bf2a21ebe1028e9c14f097884-2144x1388.png) 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!