How to add category schema to index page in Sanity e-commerce site?
I can help you restructure your e-commerce site to display categories on the home page instead of all products! This is a common pattern for e-commerce sites and works great with Sanity's flexible content structure.
Here's how to achieve this:
1. Schema Setup
First, make sure your product schema references categories. Your product schema should include a reference to categories:
// schemas/product.js
export default {
name: 'product',
title: 'Product',
type: 'document',
fields: [
// ... other fields
{
name: 'category',
title: 'Category',
type: 'reference',
to: [{type: 'category'}]
}
]
}2. Update Your Home Page (index.js)
Modify your getServerSideProps or getStaticProps to fetch categories instead of products:
// pages/index.js
import { client } from '../lib/client';
export const getStaticProps = async () => {
// Fetch categories instead of products
const categoriesQuery = '*[_type == "category"]';
const categories = await client.fetch(categoriesQuery);
return {
props: { categories }
}
}
const Home = ({ categories }) => (
<div>
<div className="products-container">
{categories?.map((category) => (
<Link href={`/category/${category.slug.current}`} key={category._id}>
<div className="product-card">
<img
src={urlFor(category.image)}
width={250}
height={250}
className="product-image"
/>
<p className="product-name">{category.name}</p>
</div>
</Link>
))}
</div>
</div>
);3. Create a Dynamic Category Page
Create a new file pages/category/[slug].js to display products filtered by category:
// pages/category/[slug].js
import React from 'react';
import { client } from '../../lib/client';
import { Product } from '../../components'; // Reuse the Product component
const CategoryPage = ({ products, category }) => {
return (
<div>
<div className="products-heading">
<h2>{category.name}</h2>
<p>{category.description}</p>
</div>
<div className="products-container">
{products?.map((product) => (
<Product key={product._id} product={product} />
))}
</div>
</div>
);
};
export const getStaticPaths = async () => {
const query = `*[_type == "category"] {
slug {
current
}
}`;
const categories = await client.fetch(query);
const paths = categories.map((category) => ({
params: {
slug: category.slug.current
}
}));
return {
paths,
fallback: 'blocking'
}
}
export const getStaticProps = async ({ params: { slug } }) => {
// Fetch the category details
const categoryQuery = `*[_type == "category" && slug.current == '${slug}'][0]`;
const category = await client.fetch(categoryQuery);
// Fetch products that reference this category
const productsQuery = `*[_type == "product" && references(*[_type=="category" && slug.current == '${slug}']._id)]`;
const products = await client.fetch(productsQuery);
return {
props: { products, category }
}
}
export default CategoryPage;4. Keep Your Existing Product Page
Your existing pages/product/[slug].js should continue to work as-is, since you're just adding a new layer of navigation above it.
Key Points:
- Home page (
/) now shows categories - Category page (
/category/[slug]) shows products filtered by that category - Product page (
/product/[slug]) shows individual product details (unchanged)
The GROQ query *[_type == "product" && references(*[_type=="category" && slug.current == '${slug}']._id)] is doing the heavy lifting here - it finds all products that reference the category you're viewing.
You can reuse most of the styling and components from the adrianhajdin/ecommerce_sanity_stripe repo, just swap out what data you're fetching and displaying on each page. The Product component from the tutorial can be reused on your category pages to maintain consistent styling.
If you're using Next.js 13+ with the App Router instead of the Pages Router, the structure would be similar but using app/category/[slug]/page.js instead. Let me know your schema files and I can help you refine this further!
Show original thread17 replies
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.