🚧 Pardon our dust, these docs are currently under construction 🚧

Gatsby Theme Shopify Manager

The easiest way to build a Shopify store on Gatsby.

Gatsby-theme-shopify-manager is an opinionated Gatsby theme that does the heavy lifting for you in setting up a Shopify store on Gatsby. It provides a foundation to build a fast Shopify shop on top of.

Want to see this in action? This page is a working example, as well as documentation. Read on to see it!

Getting Started

To start using the theme, install it with your package manager of choice:

yarn add gatsby-theme-shopify-manager

To start using it, open your gatsby-config file and include your Shop name and access token from the Storefront API.

{
  resolve: `gatsby-theme-shopify-manager`,
  options: {
    shopName: 'your shop name',
    accessToken: 'your storefront API access token',
  },
},

When you use this theme, it automatically includes gatsby-source-shopify and sets it up for you. These options are passed to that package, as well as used internally to create a connection to Shopify using shopify-buy.

Configuration options

keyrequireddescription
shopNameyesThis is the first part of the default Shopify domain. If your domain is my-store.myshopify.com, the shopName would be my-shop.
accessTokenyesThis is the Storefront API token that you get when you make a new Shopify app.

Context Provider

The Shopify buy client and current cart state are managed using React context. By default, the application is wrapped by the Provider and the shopName and accessToken are pulled from the config options and passed to it. However, in some cases, it might be preferable to manage the provider.

To use the provider, import it and pass shopName and accessToken to it as props.

import React from 'react';
import {ContextProvider} from 'gatsby-theme-shopify-manager';
export const App = ({children}) => {
  const shopName = 'some-shop-name';
  const accessToken = 'some-access-token';

  return (
    <ContextProvider shopName={shopName} accessToken={accessToken}>
      {children}
    </ContextProvider>
  );
};

Hooks

The main export of this package are the hooks that you can use. Here are the hooks you can use:

useCart()

The most basic hook is the useCart() hook. This hook gives you access to the current cart state (or null, if there is no cart state). From this object you can get access to the line items, the total amounts, and additional checkout-related information.

The cart object is currently null.

import React from 'react';
import {useCart} from 'gatsby-theme-shopify-manager';

export function ExampleUseCart() {
  const cart = useCart();

  if (cart == null) {
    return <p>The cart object is currently null.</p>;
  }

  const cartDate = new Date(cart.createdAt).toLocaleDateString();

  return (
    <p>
      Your cart was created on {cartDate}.
      <br />
      You have ${cart.totalPrice} worth of products in your cart.
    </p>
  );
}

useCartItems()

The useCartItems() hook provides access to the items currently in the cart. This hook always returns an array.

Your cart is empty.

import React from 'react';
import {useCartItems} from 'gatsby-theme-shopify-manager';

export function ExampleUseCartItems() {
  const cartItems = useCartItems();

  if (cartItems.length < 1) {
    return <p>Your cart is empty.</p>;
  }

  return (
    <>
      <p>Your cart has the following items:</p>
      <ul>
        {cartItems.map((lineItem) => (
          <li key={lineItem.title}>
            {lineItem.title} - {lineItem.variant.title}
          </li>
        ))}
      </ul>
    </>
  );
}

useCartCount()

The useCartCount() hook provides the number of items currently in the cart. This hook returns 0 if the cart is null (and will always return a number). This method does not return just cartItems.length, but sums the quantity of each variant in the cart.

Your cart has 0 items.

import React from 'react';
import {useCartCount} from 'gatsby-theme-shopify-manager';

export function ExampleUseCartCount() {
  const cartCount = useCartCount();

  return <p>Your cart has {cartCount} items.</p>;
}

useCheckoutUrl()

The useCheckoutUrl() hook provides the checkout url that is associated with the current cart. It returns null when the cart is null, and otherwise returns a string.

There is no active checkout.

import React from 'react';
import {useCheckoutUrl} from 'gatsby-theme-shopify-manager';

export function ExampleUseCheckoutUrl() {
  const checkoutUrl = useCheckoutUrl();

  return checkoutUrl == null ? (
    <p>There is no active checkout.</p>
  ) : (
    <p>
      <a href={checkoutUrl} target="_blank" rel="noopener noreferrer">
        Complete Your Order →
      </a>
    </p>
  );
}

useAddItemsToCart()

The useAddItemsToCart hook allows you to add multiple items to the cart at a single time. The hook returns a function that accepts an array of objects with keys variantId and quantity. It returns a void promise that will throw if it encounters an error. You can optionally include an array of customAttributes with each item.

There are currently 0 items in your cart.

import React from 'react';
import {Button} from 'theme-ui';
import {useAddItemsToCart, useCartCount} from 'gatsby-theme-shopify-manager';

export function ExampleUseAddItemsToCart() {
  const cartCount = useCartCount();
  const addItemsToCart = useAddItemsToCart();

  async function addToCart() {
    const items = [
      {
        variantId: 'some_variant_id',
        quantity: 1,
      },
    ];

    try {
      await addItemsToCart(items);
      alert('Successfully added that item to your cart!');
    } catch {
      alert('There was a problem adding that item to your cart.');
    }
  }

  return (
    <>
      <p>There are currently {cartCount} items in your cart.</p>
      <Button onClick={addToCart}>
        Add items to your cart
      </Button>
    </>
  );
}

useAddItemToCart()

The useAddItemToCart is similar to the useAddItemsToCart, but is only for a single item at a time. The hook returns a function that accepts three arguments: variantId, quantity, and (optionally) an array of customAttributes.

There are currently 0 items in your cart.

import React from 'react';
import {Button} from 'theme-ui';
import {useAddItemToCart, useCartCount} from 'gatsby-theme-shopify-manager';

export function ExampleUseAddItemToCart() {
  const cartCount = useCartCount();
  const addItemToCart = useAddItemToCart();

  async function addToCart() {
    const variantId = 'some_variant_id';
    const quantity = 1;

    try {
      await addItemToCart(variantId, quantity);
      alert('Successfully added that item to your cart!');
    } catch {
      alert('There was a problem adding that item to your cart.');
    }
  }

  return (
    <>
      <p>There are currently {cartCount} items in your cart.</p>
      <Button onClick={addToCart}>
        Add an item to your cart
      </Button>
    </>
  );
}

useRemoveItemsFromCart()

The useRemoveItemFromCart hook allows you to remove multiple items from the cart at a single time. The hook returns a function that accepts an array of variantId strings. It returns a void promise that will throw if it encounters an error.

Your cart is empty.

import React from 'react';
import {Button} from 'theme-ui';
import {
  useRemoveItemsFromCart,
  useCartItems,
} from 'gatsby-theme-shopify-manager';

export function ExampleUseRemoveItemsFromCart() {
  const cartItems = useCartItems();
  const removeItemsFromCart = useRemoveItemsFromCart();

  async function removeFromCart() {
    if (cartItems.length < 1) {
      return;
    }
    const variantId = cartItems[0].variant.id;

    try {
      await removeItemsFromCart([variantId]);
      alert('Successfully removed an item from your cart!');
    } catch {
      alert('There was a problem removing that item from your cart.');
    }
  }

  const cartMarkup =
    cartItems.length > 0 ? (
      <>
        <p>Your cart has the following items:</p>
        <ul>
          {cartItems.map((lineItem) => (
            <li key={lineItem.title}>
              {lineItem.title} - {lineItem.variant.title}
            </li>
          ))}
        </ul>
      </>
    ) : (
      <p>Your cart is empty.</p>
    );

  return (
    <>
      {cartMarkup}
      <Button onClick={removeFromCart} sx={{mb: 3}}>
        Remove items from your cart
      </Button>
    </>
  );
}

useRemoveItemFromCart()

The useRemoveItemFromCart is similar to the useRemoveItemsFromCart hook, but is only for a single item at a time. The hook returns a function that accepts a single argument: variantId.

Your cart is empty.

import React from 'react';
import {Button} from 'theme-ui';
import {
  useRemoveItemFromCart,
  useCartItems,
} from 'gatsby-theme-shopify-manager';

export function ExampleUseRemoveItemFromCart() {
  const cartItems = useCartItems();
  const removeItemFromCart = useRemoveItemFromCart();

  async function removeFromCart() {
    if (cartItems.length < 1) {
      return;
    }
    const variantId = cartItems[0].variant.id;

    try {
      await removeItemFromCart(variantId);
      alert('Successfully removed an item from your cart!');
    } catch {
      alert('There was a problem removing that item from your cart.');
    }
  }

  const cartMarkup =
    cartItems.length > 0 ? (
      <>
        <p>Your cart has the following items:</p>
        <ul>
          {cartItems.map((lineItem) => (
            <li key={lineItem.title}>
              {lineItem.title} - {lineItem.variant.title}
            </li>
          ))}
        </ul>
      </>
    ) : (
      <p>Your cart is empty.</p>
    );

  return (
    <>
      {cartMarkup}
      <Button onClick={removeFromCart} sx={{mb: 3}}>
        Remove item from your cart
      </Button>
    </>
  );
}

useUpdateItemQuantity()

The useUpdateItemQuantity() hook returns a function that updates the quantity of a lineitem currently in the cart. The returned function accepts two arguments: variantId and quantity. It returns a void Promise that throws if it encounters an error. If 0 is passed in as the quantity, it removes the item from the cart.

Your cart is empty.

import React, {useState} from 'react';
import {Flex, Button, Input} from 'theme-ui';
import {
  useUpdateItemQuantity,
  useCartItems,
} from 'gatsby-theme-shopify-manager';

export function ExampleUseUpdateItemQuantity() {
  const [quantity, setQuantity] = useState(1);
  const [item] = useCartItems();
  const updateItemQuantity = useUpdateItemQuantity();

  async function updateQuantity() {
    if (item == null) {
      return;
    }

    const variantId = item.variant.id;

    try {
      await updateItemQuantity(variantId, quantity);
      alert('Successfully updated the item quantity!');
    } catch {
      alert("There was a problem updating that item's quantity.");
    }
  }

  const itemMarkup =
    item == null ? (
      <p>Your cart is empty.</p>
    ) : (
      <p>
        {item.title} - {item.variant.title} ({item.quantity})
      </p>
    );

  const formMarkup = (
    <Flex sx={{alignItems: 'flex-start'}} as="form" onSubmit={updateQuantity}>
      <Input
        sx={{width: '50px', mr: 3}}
        type="number"
        defaultValue={quantity}
        onChange={(event) => setQuantity(Number(event.target.value))}
      />
      <Button sx={{mb: 3}}>Update quantity</Button>
    </Flex>
  );

  return (
    <>
      {itemMarkup}
      {formMarkup}
    </>
  );
}

Escape Hooks

In addition to the normal hooks, there are two 'escape' hooks. These hooks allow access to setting the cart state and the client object that is used to interact with Shopify. It's important to note that these are considered experiemental–using these hooks may result in unintended side-effects.

useClientUnsafe

The useClientUnsafe hook returns the client object currently held in the context. From there you can call methods on it to enable more functionality. Shopify has all the documentation for what you can do with the client object. Example usage:

import React from 'react';
import {useClientUnsafe} from 'gatsby-theme-shopify-manager';

export function ExampleUseClientUnsafe() {
  const client = useClientUnsafe();
  // do work with the client here
}

useSetCartUnsafe

The useSetCartUnsafe returns a function that allows the user to set the current cart state. You can use it similar to the function returned from a useState destructure. This is useful for interactions with the client object that return an updated cart object. Example usage:

import React from 'react';
import {useClientUnsafe, useSetCartUnsafe} from 'gatsby-theme-shopify-manager';

export function ExampleUseSetCartUnsafe() {
  const client = useClientUnsafe();
  const setCart = useSetCartUnsafe();

  async function changeCart() {
    const newCart = await client.doSomeMethodThatReturnsACartObject();
    setCart(newCart);
  }

  changeCart();
}