Giter VIP home page Giter VIP logo

hydrogen-plugin-sanity's Introduction

Sanity plugin for Hydrogen

๐Ÿšจ This package is deprecated ๐Ÿšจ

We've deprecated this plugin as it promoted conducting larger fetches (at the page level) and using shared context. This was often difficult to reason about and worked against the benefits of using React Server Components.

We recommend you keep fetching logic scoped to specific components that need them, even when deeply nested in Portable Text. You'll have much better control over exactly how much you fetch from the Storefront API whilst being able to define caching and prefetching logic on a per-component basis.

To run GROQ queries against your Sanity dataset, use the official @sanity/client library and fetch within Hydrogen's useQuery:

// MyServerComponent.server.jsx
import sanityClient from '@sanity/client'
import {useQuery} from '@shopify/hydrogen'

const query = `*[_type == 'page' && slug.current == $slug]`
const params = {slug: 'about'}

// Configure Sanity client
const client = sanityClient({
  apiVersion: 'v2022-05-01',
  dataset: 'your-dataset',
  projectId: 'your-project-id',
  useCdn: true
})

export default function MyServerComponent() {
  const {data, error} = useQuery([query, params], async () => {
    return await client.fetch(query, params)
  })
  return <div>{JSON.stringify(data)}</div>
}

โš ๏ธ๏ธ Hydrogen is in developer preview and undergoing frequent changes. This plugin is currently compatible with @shopify/hydrogen >= 0.13.x. โš ๏ธ

Sanity is the platform for structured content that lets you build better digital experiences. Shopify customers can use Sanity Studio, our open-source content editing environment, to combine product and marketing information to build unique shopping experiences.

This plugin for Shopify's Hydrogen lets you query Sanity data, combine it with live inventory, and present that information with Hydrogen components. A useSanityQuery React hook with an API similar to useShopQuery is exposed to efficiently and ergonomically fetch data from a Sanity instance.

Getting Started

Add the plugin as a dependency to your Hydrogen project:

yarn add hydrogen-plugin-sanity # or `npm install`

Create a separate file containing your Sanity configuration (refer to @sanity/client for available options):

// sanity.config.js

export default {
  projectId: 'yourSanityProjectId',
  dataset: 'production',
  apiVersion: 'v2021-06-07'
}

These Sanity settings must be included in every hook invocation. Also keep in mind these hooks can only be run in server components.

Fetching Sanity data through GraphQL

// Using GraphQL

import {useSanityGraphQLQuery} from 'hydrogen-plugin-sanity'
import clientConfig from './sanity.config.js'

const {sanityData} = useSanityGraphQLQuery({
  query: gql`
    query homepage($homeId: String!) {
      home: Home(id: $homeId) {
        featuredProducts {
          _id
          images {
            asset {
              _id
            }
          }
        }
      }
    }
  `,
  variables: {homeId: 'homepage'},
  clientConfig
})

Fetching data through GROQ

// Using GROQ

import {useSanityQuery} from 'hydrogen-plugin-sanity'
import clientConfig from './sanity.config.js'

const {sanityData} = useSanityQuery({
  query: `*[_id == $homeId][0]{
    ...,
    featuredProducts[] {
      _id,
      images[] {
        asset {
          _id
        }
      }
    }
  }`,
  params: {homeId: 'homepage'}
  clientConfig,
})

Getting product data from Shopify

By default, the hook will automatically look for Shopify products referenced in your Sanity data and fetch them from Shopify for fresh inventory data. The resulting data will be returned through the shopifyProducts object:

import {BuyNowButton, ProductProvider} from '@shopify/hydrogen'
import clientConfig from './sanity.config.js'

const Homepage = () => {
  const {sanityData, shopifyProducts} = useSanityQuery({
    query: `*[_id == "homepage"][0]{
      ...,
      featuredProducts[] {
        _id,
        images[] {
          asset {
            _id
          }
        }
      }
    }`,
    clientConfig
  })

  return (
    <div>
      <h1>{sanityData.title}</h1>
      <div>
        {sanityData.featuredProducts.map((product) => {
          // From the product's ID in Sanity, let's get its Shopify data
          const shopifyProduct = shopifyProducts?.[product?._id]
          const firstVariant = shopifyProduct?.variants?.edges[0]?.node

          return (
            <ProductProvider value={shopifyProduct} initialVariantId={firstVariant?.id}>
              <h2>{shopifyProduct.title}</h2>
              <BuyNowButton>Buy now</BuyNowButton>
            </ProductProvider>
          )
        })}
      </div>
    </div>
  )
}

At this point, you now have both your data from Sanity and fresh product data from Storefront API that you can inject right into your Hydrogen <ProductProvider> components to take advantage of their various Product helper components.

How you choose to combine these two sources of data in your app is a matter of personal preference and project structure. Refer to our hydrogen-sanity-demo for an example of how to use this data.

Advanced product querying

You can customize what data you fetch from Shopify on a product basis with the getProductGraphQLFragment function. For example, if we're on a legal page where there's no product to buy, we can skip fetching any Shopify data:

const {handle} = useParams()

const {sanityData} = useSanityQuery({
  query: `*[
    _type == "page.legal" &&
    slug.current == $handle
  ][0]`,
  params: {
    handle
  },
  // No need to query Shopify product data
  getProductGraphQLFragment: () => false
})

getProductGraphQLFragment receives an object with shopifyId, sanityId, and occurrences - where in the data structure this product has been found - and must return either:

  • true for fetching default product data (which uses ProductProviderFragment)
  • false for avoiding fetching data for this product
  • A string with the GraphQL fragment for that product

With this, you can fetch specific product data for each use-case. For example, if a product shows up in the first section of a homepage, let's only fetch its full data if it's featured. Otherwise, grabbing its title & handle is enough as we won't offer an "Add to Cart" button for it.

In this example, let's assume the data from Sanity is as follows:

{
  "_type": "homepage",
  "firstSection": {
    "title": "Fresh out the oven",
    "products": [
      {
        "_type": "section-item",
        "_key": "7349334187288",
        "featured": false,
        "product": {
          "_id": "shopifyProduct-7349334187288",
          "store": {
            "handle": "regular-product"
          }
        }
      },
      {
        "_type": "hero-item",
        "_key": "7342335787245",
        "featured": true,
        "product": {
          "_id": "shopifyProduct-7342335787245",
          "store": {
            "handle": "special-product"
          }
        }
      }
    ]
  }
}

From this data structure, here's how we'd achieve that:

const {sanityData, shopifyProducts} = useSanityQuery({
  query: QUERY,
  getProductGraphQLFragment: ({occurrences}) => {
    // If the product ID shows up in 2+ places, fetch the default product data
    if (occurrences.length > 1) {
      return true
    }

    /* Immediate parent of where this product appears (occurrences[0][0]) -> {
      "_id": "shopifyProduct-7342335787245",
      "shopify": {
        "handle": "special-product"
      }
    }

    Parent of parent (occurrences[0][1]) -> {
      "_type": "section-item",
      featured: true,
      "product": {
        "_id": "shopifyProduct-7342335787245",
        "store": {
          "handle": "special-product"
        }
      }
    }*/
    if (occurrences[0][1]?._type === 'section-item') {
      if (!occurrences[0][1].featured) {
        //  We only want the title & handle for non-featured products
        return `
      title
      handle
      `
      }
    }

    // Get the default ProductProviderFragment otherwise
    return true
  }
})

Using useSanityQuery to fetch Sanity data only

useSanityQuery won't make any requests to the Storefront API for products if it can't find any valid references. However, you can specifically opt-out of this behavior by providing a custom getProductGraphQLFragment function which always returns false.

const {sanityData} = useSanityQuery({
  query: `
    *[_type == "page.legal"][0] {
      slug.current == $handle
    }
  `,
  params: {handle},
  // No need to query Shopify product data
  getProductGraphQLFragment: () => false
})

How it works

useSanityQuery flow diagram

Assumptions

In order for useSanityQuery to be able to identify Shopify products in your dataset and query for them on your behalf, there are two rules you must follow:

1. Your Shopify product documents in Sanity must have an _id with the following naming convention:

shopifyProduct-${ID}

Where ID is the ID of the product in Shopify. This is exposed in the URL when navigating your Shopify admin e.g. https://my-shopify-store.myshopify.com/admin/products/6639629926487

The correct document _id for the above example would be:
shopifyProduct-6639629926487

2. Your Sanity query response must return these IDs in either _id or _ref keys

These can any number of levels nested in your response. Either of the below are valid.

"result": {
  "products": {
    [
      {
        "_ref": "shopifyProduct-6639500034135",
      },
      {
        "_ref": "shopifyProduct-6639504195671",
      },
    ]
  }
}
"result": {
  "body": [
    {
      "_type": "myCustomPortableTextBlock",
      "caption": "Product caption",
      "myNestedObject": {
        "product": {
          "_ref": "shopifyProduct-6639629926487"
        }
      }
    },
  ]
}

If you're using Sanity Connect, it will automatically create documents with this naming convention by default.

hydrogen-plugin-sanity's People

Contributors

benjaminsehl avatar dependabot[bot] avatar hdoro avatar robinpyon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hydrogen-plugin-sanity's Issues

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.