How to set a slug for a product variant in Sanity and how to handle routes in Gatsby for product variants.

7 replies
Last updated: Jun 27, 2021
Hello, I have a schema known as
productVariant
with bunch of fields within, one of them being a
type
:
slug
. This
productVariant
document is referenced in my main
products.js
schema, allowing me to create variants of the product in Sanity studio.

products.js

    {
      title: 'Variants',
      type: 'array',
      of: [
        {
          title: 'Variant',
          type: 'productVariant',
        },
      ],
    },
My question is... I want a slug for my
productVariant
which i can eventually use to append to the end of my product slug on Gatsby. How can I do so in my
productVariant
schema if say, i want my
productVariant
slug to be
title
of the variant?

productVariant.js

export default {
  title: 'Product variant',
  name: 'productVariant',
  type: 'object',
  fields: [
    {
      title: 'Title',
      name: 'title',
      type: 'string',
    },
    {
      title: 'Slug',
      name: 'slug',
      type: 'slug',
    },
AI Update

Based on the documentation, you can absolutely generate a slug from the title field in your productVariant schema! The slug field type in Sanity has an options.source property that lets you specify which field to use for slug generation.

Here's how to update your productVariant.js schema:

export default {
  title: 'Product variant',
  name: 'productVariant',
  type: 'object',
  fields: [
    {
      title: 'Title',
      name: 'title',
      type: 'string',
    },
    {
      title: 'Slug',
      name: 'slug',
      type: 'slug',
      options: {
        source: 'title',
        maxLength: 96
      }
    },
  ]
}

The key part is adding options: { source: 'title' } to your slug field. This tells Sanity to use the title field as the source when you click the "Generate" button in the Studio.

Important note about nested objects: Since productVariant is defined as type: 'object' (not a document), the source option works a bit differently. When you're inside the variant object in the Studio, source: 'title' will correctly reference the title field within that same variant object.

If you need to reference the parent product's title or combine multiple fields, you can use a function instead:

options: {
  source: (doc, options) => options.parent?.title,
  // or combine fields:
  // source: (doc, options) => `${options.parent?.title} ${doc.title}`
}

The options.parent gives you access to the parent document context when working with nested objects, which is particularly useful for your use case.

Once this is set up, when you create or edit a variant in Studio, you'll see a "Generate" button next to the slug field that will automatically create a URL-friendly slug from the variant's title. The slug field automatically converts text to lowercase, replaces spaces with hyphens, and removes special characters to make it URL-safe.

Then in Gatsby, you can append this variant.slug.current to your product slug to create URLs like /products/my-product/blue-variant.

You can read more about slug field configuration in the Sanity documentation.

Two suggestions to start—and maybe you’ve already done this—but I would give your variants array in products.js a
name
and set
options.source
on your slug.
Your title will already be the title of each item in your variants array, so I don’t follow when you are asking about making the productVariant slug the title of the variant. Do you mean you want to set the title of your products document?
If that were the case, you might try something like:

// productVariant.js

export default {
  title: 'Product variant',
  name: 'productVariant',
  type: 'object',
  fields: [
    {
      title: 'Title',
      name: 'title',
      type: 'string',
    },
    {
      title: 'Slug',
      name: 'slug',
      type: 'slug',
      options: {
        source: (_, {parent}) => {
          return parent.title
        },
      },
    },
  ]
}

options.source
can be a string or a function. In this case, I wasn’t able to simply use
title
(maybe because we’re working with an object) so we use a function, destructure the second parameter to get
parent
, and then get title from that. 🤷‍♂️

// products.js

export default {
  name: 'products',
  type: 'document',
  fields: [
    {
      title: 'Variants',
      name: 'variants',
      type: 'array',
      of: [
        {
          title: 'Variant',
          type: 'productVariant',
        },
      ],
    },
  ],
  preview: {
    select: {
      variant0: 'variants.0.title',
      variant1: 'variants.1.title',
      variant2: 'variants.2.title',
      variant3: 'variants.3.title',
    },
    prepare: ({...variants}) => {
      const productVariants = Object.values(variants).filter(Boolean);
      return {
        title: `Variants: ${productVariants.length > 0 ? productVariants.join(', ') : 'None'}`,
      }
    }
  }
}
Here we get an arbitrary number of variants (I used 4), spread them into the prepare function in preview, filter out any undefined variants, and return them in a comma-separated list.

If I misunderstood what you’re after please follow up and let me know!
thanks
user A
you solved it with the first answer. I wasn't sure of how to use
source
. It wasn't as straight forward as console logging my
productVariant.js
and trying to identify the data structure to query. For example I knew I had to pass in some arguments but never knew it was
{parent}
.
now it's done and im able to set the
title
as the slug.
I'm now wondering what's the best practice on Gatsby should I wish to render a page for my product variant. I don't want the page component to change but i only want to update certain fields. E.g. Price. I'd also like to navigate to a new route e.g.
/product/variant-a
from
/product
.
I used do this in react a couple years ago with
react-router
? I remember passing the props and then setting the state on the component with the new props or something along those lines. However with Gatsby im not sure what should be the approach. I pinged on the Gatsby discord but havent received a reply so I'm trying my luck here 🤞
Thank you, that’s good feedback and I’ll make sure to pass it along. I think I originally got it from the second code block here but I’m not sure it’s listed out anywhere.
For the routes, perhaps you could take a look at the
Kitchen Sink starter , which separates the pages from the routes. Is that what you’re after?
user A
What i'm trying to achieve is more like what you see here:

https://www.patternbrands.com/shop/nesting-trays/?variant=38044220195013


?variant=38044220195013
is one of the color of the products, but on the same page I could click on another color which then changes the url to
?variant=38044220227781
instead. However, it's still largely the same react component with minor changes such as the image itself.

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?