Building a Scalable E-commerce Solution with Astro, Sanity, and Stripe
Building e-commerce sites often means wrestling with product variants and complex pricing. When a jewelry client needed a system that could handle dynamic product attributes while maintaining performance, I discovered how powerful the combination of Astro, Sanity CMS, and Stripe could be.
Here’s how I built a scalable online jewelry store that solved the client’s core challenge: managing complex product variants with flexible attributes through a headless CMS.
The Challenge: Dynamic Product Variants
The client’s requirements:
- Products with multiple variants (material, size, design)
- Some attributes affect price (materials: +$50 for gold)
- Content editors need to add new attributes without developer involvement
- Fast, scalable site with server-side rendering
The Tech Stack
- Astro: Server-side rendering with React islands
- Sanity CMS: Flexible content management and product data
- Stripe: Payment processing
- Node.js hosting: Server-side rendering with the Node adapter
The Breakthrough: Separate Definitions from Products
The Core Insight
Instead of hardcoding attributes in each product, I created reusable attribute definitions that products reference. This lets content editors add new attributes without touching code.
Note: Code examples below are simplified for clarity. The actual implementation uses Sanity’s
defineType
anddefineField
syntax with additional validation and UI components.
Step 1: Create Reusable Attributes
// Define "Material" once, use everywhere
{
name: "Material",
predefinedValues: ["Silver", "Gold", "Platinum"]
}
Step 2: Products Reference Attributes + Add Modifiers
// Product links to "Material" definition + adds pricing
{
definition: "Material", // Reference to definition above
selectedValues: ["Silver", "Gold"], // Which values to use
modifiers: [
{ field: "price", type: "plus", value: 50, appliesTo: ["Gold"] } // +$50 for Gold
]
}
Step 3: Generate All Combinations
// Final variants with calculated prices
[
{ sku: "ring-silver", price: 100, attributes: [{ name: "Material", value: "Silver" }] },
{ sku: "ring-gold", price: 150, attributes: [{ name: "Material", value: "Gold" }] }
]
How Price Modifiers Work
Content editors can set modifiers that automatically adjust price, stock, or images:
// Modifier examples
{ field: "price", type: "plus", value: 50, appliesTo: ["Gold"] } // +$50 for Gold
{ field: "price", type: "set", value: 200, appliesTo: ["Platinum"] } // $200 for Platinum
{ field: "variantImageIndex", type: "set", value: 2, appliesTo: ["Gold"] } // Use image #2 for Gold
Frontend: Dynamic Variant Selection
The React component automatically generates selectors by extracting attributes from variants:
function ProductVariantSelector({ product }) {
// Extract unique attributes from all variants
const availableAttributes = useMemo(() => {
const attrs = {};
product.variants.forEach(variant => {
variant.attributes?.forEach(attr => {
if (!attrs[attr.name]) attrs[attr.name] = new Set();
attrs[attr.name].add(attr.value);
});
});
return Object.entries(attrs); // [["Material", ["Silver", "Gold"]], ["Size", ["S", "M"]]]
}, [product.variants]);
// Generate selectors automatically
return (
<div>
{availableAttributes.map(([name, values]) => (
<select key={name}>
<option>Select {name}</option>
{Array.from(values).map(value => (
<option key={value}>{value}</option>
))}
</select>
))}
</div>
);
}
The Magic: Automatic Variant Generation
Content Editor Workflow
- Create Attribute Definitions: Define “Material” with values [“Silver”, “Gold”, “Platinum”]
- Create Product: Select which values to use, set pricing modifiers
- Hit “Generate Variants”: Sanity action creates all combinations automatically
- Frontend Updates: React component adapts to new attributes instantly
Behind the Scenes
The system generates all possible combinations and applies modifiers:
// Material: ["Silver", "Gold"] + Size: ["S", "M"] = 4 variants
combinations = [
[{ name: "Material", value: "Silver" }, { name: "Size", value: "S" }],
[{ name: "Material", value: "Silver" }, { name: "Size", value: "M" }],
[{ name: "Material", value: "Gold" }, { name: "Size", value: "S" }],
[{ name: "Material", value: "Gold" }, { name: "Size", value: "M" }]
]
// Apply modifiers: Gold gets +$50
variants = [
{ sku: "ring-silver-s", price: 100, attributes: [...] },
{ sku: "ring-silver-m", price: 100, attributes: [...] },
{ sku: "ring-gold-s", price: 150, attributes: [...] }, // +$50 for Gold
{ sku: "ring-gold-m", price: 150, attributes: [...] } // +$50 for Gold
]
Results: Zero-Code Attribute Management
What Content Editors Can Now Do
- Define new attributes (color, style, gemstone) through Sanity Studio
- Set price modifiers that only apply to specific values (+$50 for “Gold”)
- Generate hundreds of variants from 3-4 attributes automatically
- Control which combinations are created by selecting specific values
Why This Works
The key insight: treat attributes as reusable data, not hardcoded properties.
- Attribute Definitions: Create once, use everywhere
- Product References: Link to definitions + add pricing modifiers
- Generated Variants: All combinations with calculated prices
- Dynamic Frontend: Component adapts to any attribute structure
Content editors can add “Gemstone” or “Engraving” attributes tomorrow, and the frontend automatically generates new selectors and pricing logic. No developer needed.
This scales from simple products with 2-3 variants to complex jewelry with dozens of combinations - all managed through the CMS interface.
Loom video
Check how it works in action!