Building a Scalable E-commerce Solution with Astro, Sanity, and Stripe

astro sanity stripe ecommerce ssr

Jewelry store product variants interface

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 and defineField 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

  1. Create Attribute Definitions: Define “Material” with values [“Silver”, “Gold”, “Platinum”]
  2. Create Product: Select which values to use, set pricing modifiers
  3. Hit “Generate Variants”: Sanity action creates all combinations automatically
  4. 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

  1. Define new attributes (color, style, gemstone) through Sanity Studio
  2. Set price modifiers that only apply to specific values (+$50 for “Gold”)
  3. Generate hundreds of variants from 3-4 attributes automatically
  4. 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!