Home

Awesome

[!NOTE]
This repository for a Hydrogen front end is no longer maintained, but the connecting plugin Sanity Connect for Shopify as well as the shopify starter template for Sanity Studio are still in active development. See the Sanity Templates page for other ecommerce templates.

AKVA - An example storefront powered by Sanity + Hydrogen

This demo is compatible with @shopify/hydrogen >= 2023.10.0 built on Remix.

For a more complex reference example, please see our demo-ecommerce repo which features a monorepo with an embedded Sanity Studio, full live preview, more content models and added internationalisation. For the legacy Hydrogen v1 template, please refer to the hydrogen-v1 branch.

<img src="https://user-images.githubusercontent.com/209129/173065853-77b26be2-dd15-4b4d-8164-850e70247b88.png" width="1000" />

Demo | Sanity Studio | Sanity Connect for Shopify

About

AKVA is our customized Hydrogen starter that presents a real-world example of how Sanity and Structured Content can elevate your custom Shopify storefronts.

It's designed to be used alongside our pre-configured Studio and Sanity Connect for Shopify, which syncs products and collections from your Shopify storefront to your Sanity dataset.

This starter showcases a few patterns you can adopt when creating your own custom storefronts. Use Sanity and Hydrogen to delight customers with rich, shoppable editorial experiences that best tell your story.

Features

View the feature gallery

This TypeScript demo adopts many of Hydrogen's framework conventions and third-party libraries. If you've used Hydrogen then you should hopefully feel at home here.

Fetching Sanity data

This demo comes preconfigured to use hydrogen-sanity, which adds a Sanity client to the Remix context. This enables you to fetch content from Sanity in Remix loaders and actions.

In addition to this, we've created a query utility, which uses Hydrogen's caching strategies to reduce the number of calls to Sanity's API. If no strategy is provided to the cache option, then the Hydrogen CacheLong() strategy will be used by default.

It's possible to make calls to the Sanity API either with query:

import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
import type {SanityProductPage} from '~/lib/sanity';

const QUERY = `*[_type == 'product' && slug.current == $slug]`;

export async function loader({params, context}: LoaderFunctionArgs) {
  const cache = context.storefront.CacheLong();

  const sanityContent = await context.sanity.query<SanityProductPage>({
    query: QUERY,
    params: {
      slug: params.handle,
    },
    cache,
  });

  return json({sanityContent});
}

or directly with the Sanity client:

// <root>/app/routes/($lang).products.$handle.tsx
import {useLoaderData} from '@remix-run/react';
import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
import type {SanityProductPage} from '~/lib/sanity';

const QUERY = `*[_type == 'product' && slug.current == $slug]`;

export async function loader({params, context}: LoaderFunctionArgs) {
  const sanityContent = await context.sanity.client.fetch<SanityProductPage>(
    QUERY,
    {
      slug: params.handle,
    },
  );

  return json({sanityContent});
}
export default function Product() {
  const {sanityContent} = useLoaderData<typeof loader>();

  // ...
}

This uses our official @sanity/client library, so it supports all the methods you would expect to interact with Sanity API's

You can also use the defer and Await utilities from Remix to prioritize critical data:

// <root>/app/routes/($lang).products.$handle.tsx
import {Suspense} from 'react';
import {Await, useLoaderData} from '@remix-run/react';
import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
import type {SanityProductPage, LessImportant} from '~/lib/sanity';

const QUERY = `*[_type == 'product' && slug.current == $slug]`;
const ANOTHER_QUERY = `*[references($id)]`;

export async function loader({params, context}: LoaderFunctionArgs) {
  /* Await the important content here */
  const sanityContent = await context.sanity.query<SanityProductPage>({
    query: QUERY,
    params: {
      slug: params.handle,
    },
  });

  /* This can wait - so don't await it - keep it as a promise */
  const moreSanityContent = context.sanity.query<LessImportant>({
    query: ANOTHER_QUERY,
    params: {
      id: sanityContent._id,
    },
  });

  return defer({sanityContent});
}
export default function Product() {
  const {sanityContent, moreSanityContent} = useLoaderData<typeof loader>();

  return (
    <div>
      <Content value={sanityContent} />
      {/* Wrap promises in a Suspense fallback and await them */}
      <Suspense fallback={<Spinner />}>
        <Await resolve={moreSanityContent}>
          {(content) => <MoreContent value={content} />}
        </Await>
      </Suspense>
    </div>
  );
}

Live Preview

In addition to providing a Sanity Client, hydrogen-sanity can utilize Sanity's realtime content platform to give editors live-as-you-type previewing of their content. That way they can see, in context, how their changes will appear directly in the storefront.

You can read more about configuration in the hydrogen-sanity documentation.

This demo is set up with an example of live preview on the ($lang)._index.tsx route.

Opinions

We've taken the following opinions on how we've approached this demo.

<details> <summary><strong>Shopify is the source of truth for non-editorial content</strong></summary> </details> <details> <summary><strong>Shopify data stored in our Sanity dataset is used to improve the editor experience</strong></summary> </details> <details> <summary><strong>Collections are managed entirely by Shopify</strong></summary> </details> <details> <summary><strong>Product options are customized in Sanity</strong></summary> </details> <details> <summary><strong>We don't surface Shopify HTML descriptions and metatags</strong></summary> </details> <details> <summary><strong>Non-product (regular) pages are managed entirely by Sanity</strong></summary> </details> <details> <summary><strong>We query our Sanity dataset when building sitemap.xml entries</strong></summary> </details>

Analytics

We've set up basic Shopify Analytics on this demo. The hasUserConsent boolean in <root>/app/root.tsx is set to true - you'll likely need to set up user consent based on the relevant regulations for your storefront.

Getting started

Requirements:

Getting Started

  1. Create a .env file, based on the .env.template file.

  2. Install dependencies and start the development server

    npm i
    npm run dev
    
  3. Visit the development environment running at http://localhost:3000.

For information on running production builds and deployment, see the Hydrogen documentation.

License

This repository is published under the MIT license.