Headless Commerce
Shopify Headless Commerce: Ein Kosten-Guide 2025

Was ist Shopify Headless Commerce eigentlich?
Headless Commerce bedeutet: Sie trennen das Frontend (was Ihre Kunden sehen) vom Backend (Shopify als Datenquelle).
Klassisches Shopify:
Theme (Liquid) + Shopify Backend = Alles in einem
Headless Shopify:
Custom Frontend (Next.js/React) → Shopify Storefront API → Shopify Backend
Warum überhaupt Headless?
Sie brauchen Headless, wenn:
- ✅ Vollständige Design-Kontrolle ohne Theme-Grenzen
- ✅ Perfekte Core Web Vitals (90+ Lighthouse Score)
- ✅ Multi-Channel (Web, App, Kiosk) mit gleicher Datenbasis
- ✅ Komplexe Internationalisierung (mehrsprachig, mehrere Währungen)
- ✅ Custom User Experience (Konfiguratoren, AR, VR)
- ✅ Maximale Performance durch Edge-Rendering
Sie brauchen KEIN Headless, wenn:
- ❌ Ein Theme von Shopify Experts reicht
- ❌ Budget < €15.000
- ❌ Sie schnell launchen müssen (< 2 Monate)
- ❌ Ihr Team keine Frontend-Entwickler hat
- ❌ Standard-Funktionalität ausreicht
Wichtig: Headless ist kein Selbstzweck. Es löst spezifische Probleme – bringt aber auch mehr Komplexität.
Die komplette Kostenaufstellung
Initial Setup (einmalige Kosten)
Projekt-Setup & Architektur
- • Tech-Stack-Entscheidung (Next.js 15, Remix, Astro)
- • CI/CD Pipeline einrichten (GitHub Actions, Vercel)
- • Staging/Production-Umgebungen
- • Shopify Storefront API Setup
- • Caching-Strategy definieren
Frontend-Entwicklung (Next.js 15)
Shopify Storefront API Integration
- • GraphQL-Queries optimieren (Fragment-Strategy)
- • Webhooks & Real-time Updates
- • Custom Metafields & Markets API
- • Rate Limiting & Error Handling
- • Customer Account API Integration
Performance-Optimierung
- • Image-Optimierung (next/image, Sharp)
- • Code-Splitting & Lazy-Loading
- • Server-Side Rendering (SSR/ISR)
- • Edge-Caching mit CDN
- • Bundle-Size-Optimierung
Testing & QA
- • Cross-Browser-Testing (Chrome, Safari, Firefox)
- • Mobile-Responsiveness (iOS, Android)
- • Performance-Testing (Lighthouse, WebPageTest)
- • E2E-Tests (Playwright, Cypress)
Der Tech-Stack im Detail
1. Frontend: Next.js 15 (App Router)
Warum Next.js 15?
- ✅ Server Components = schnellere Ladezeiten
- ✅ Built-in Image-Optimierung
- ✅ Incremental Static Regeneration (ISR)
- ✅ Beste Developer Experience
- ✅ Turbopack für blitzschnelle Builds
- ✅ Partial Prerendering (PPR) für optimale Performance
// app/products/[handle]/page.tsx
import { getProduct, getRelatedProducts } from '@/lib/shopify';
import { ProductImages } from '@/components/ProductImages';
import { AddToCartButton } from '@/components/AddToCartButton';
import { Metadata } from 'next';
interface Props {
params: { handle: string };
searchParams: { variant?: string };
}
// Dynamic Metadata für SEO
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const product = await getProduct(params.handle);
return {
title: `${product.title} | Ihr Shop`,
description: product.seo.description || product.description,
openGraph: {
title: product.title,
description: product.seo.description,
images: [{ url: product.featuredImage.url }],
},
};
}
// Server Component (default in App Router)
export default async function ProductPage({ params, searchParams }: Props) {
// Parallel Data Fetching
const [product, relatedProducts] = await Promise.all([
getProduct(params.handle),
getRelatedProducts(params.handle),
]);
// Variant aus URL oder erste Variant
const selectedVariant =
product.variants.find((v) => v.id === searchParams.variant) ||
product.variants[0];
return (
<main className="container mx-auto px-4 py-8">
<div className="grid md:grid-cols-2 gap-8">
{/* Product Images - Client Component */}
<ProductImages images={product.images} />
<div>
<h1 className="text-3xl font-bold mb-4">{product.title}</h1>
<div className="text-2xl font-semibold mb-4">
{selectedVariant.price.amount} {selectedVariant.price.currencyCode}
</div>
{/* Variant Selector */}
<div className="mb-6">
{product.options.map((option) => (
<div key={option.id} className="mb-4">
<label className="block text-sm font-medium mb-2">
{option.name}
</label>
{/* Client Component für Interaktivität */}
<VariantSelector
option={option}
variants={product.variants}
currentVariant={selectedVariant.id}
/>
</div>
))}
</div>
{/* Add to Cart - Client Component */}
<AddToCartButton
variant={selectedVariant}
availableForSale={selectedVariant.availableForSale}
/>
{/* Product Description */}
<div
className="prose mt-8"
dangerouslySetInnerHTML={{ __html: product.descriptionHtml }}
/>
</div>
</div>
{/* Related Products */}
{relatedProducts.length > 0 && (
<section className="mt-16">
<h2 className="text-2xl font-bold mb-6">Ähnliche Produkte</h2>
<div className="grid md:grid-cols-4 gap-6">
{relatedProducts.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</div>
</section>
)}
</main>
);
}
// ISR: Seite wird bei Bedarf neu generiert (nach 3600 Sekunden)
export const revalidate = 3600; // 1 StundeModerne Architektur-Patterns für 2025:
1. Fragment-Strategy für konsistente Daten
// lib/shopify/fragments.ts
export const PRODUCT_FRAGMENT = `
fragment ProductFragment on Product {
id
title
handle
description
descriptionHtml
totalInventory
vendor
productType
publishedAt
createdAt
updatedAt
priceRange {
minVariantPrice {
amount
currencyCode
}
maxVariantPrice {
amount
currencyCode
}
}
featuredImage {
url
altText
width
height
}
images(first: 20) {
edges {
node {
url
altText
width
height
}
}
}
seo {
title
description
}
options {
id
name
values
}
variants(first: 250) {
edges {
node {
id
title
sku
availableForSale
price {
amount
currencyCode
}
compareAtPrice {
amount
currencyCode
}
selectedOptions {
name
value
}
image {
url
altText
}
}
}
}
}
`;
// Query die das Fragment nutzt
export const GET_PRODUCT_QUERY = `
${PRODUCT_FRAGMENT}
query getProduct($handle: String!) {
product(handle: $handle) {
...ProductFragment
}
}
`;
// Vorteil: Konsistente Datenstruktur in der gesamten App
// Kein "Vergessen" von wichtigen Feldern
2. Server Actions für Cart-Management
// app/actions/cart.ts
'use server';
import { cookies } from 'next/headers';
import { revalidateTag } from 'next/cache';
const SHOPIFY_STOREFRONT_API = process.env.SHOPIFY_STOREFRONT_API_URL!;
const SHOPIFY_STOREFRONT_TOKEN = process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN!;
async function shopifyFetch(query: string, variables = {}) {
const res = await fetch(SHOPIFY_STOREFRONT_API, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Shopify-Storefront-Access-Token': SHOPIFY_STOREFRONT_TOKEN,
},
body: JSON.stringify({ query, variables }),
});
return res.json();
}
// Cart erstellen oder abrufen
export async function getCart() {
const cookieStore = cookies();
const cartId = cookieStore.get('cartId')?.value;
if (!cartId) {
return null;
}
const { data } = await shopifyFetch(
`
query getCart($cartId: ID!) {
cart(id: $cartId) {
id
checkoutUrl
totalQuantity
cost {
subtotalAmount {
amount
currencyCode
}
totalAmount {
amount
currencyCode
}
}
lines(first: 100) {
edges {
node {
id
quantity
merchandise {
... on ProductVariant {
id
title
price {
amount
currencyCode
}
product {
title
handle
featuredImage {
url
}
}
}
}
}
}
}
}
}
`,
{ cartId }
);
return data.cart;
}
// Produkt zum Cart hinzufügen
export async function addToCart(variantId: string, quantity: number = 1) {
const cookieStore = cookies();
let cartId = cookieStore.get('cartId')?.value;
// Cart erstellen wenn noch nicht vorhanden
if (!cartId) {
const { data } = await shopifyFetch(`
mutation cartCreate {
cartCreate {
cart {
id
}
}
}
`);
cartId = data.cartCreate.cart.id;
cookieStore.set('cartId', cartId, {
expires: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 Tage
});
}
// Item hinzufügen
const { data } = await shopifyFetch(
`
mutation cartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
cartLinesAdd(cartId: $cartId, lines: $lines) {
cart {
id
}
userErrors {
field
message
}
}
}
`,
{
cartId,
lines: [
{
merchandiseId: variantId,
quantity,
},
],
}
);
// Cache invalidieren
revalidateTag('cart');
return data.cartLinesAdd.cart;
}
// Cart-Item entfernen
export async function removeFromCart(lineId: string) {
const cookieStore = cookies();
const cartId = cookieStore.get('cartId')?.value;
if (!cartId) {
return null;
}
const { data } = await shopifyFetch(
`
mutation cartLinesRemove($cartId: ID!, $lineIds: [ID!]!) {
cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
cart {
id
}
userErrors {
field
message
}
}
}
`,
{
cartId,
lineIds: [lineId],
}
);
revalidateTag('cart');
return data.cartLinesRemove.cart;
}
// Cart-Item Quantity updaten
export async function updateCartQuantity(lineId: string, quantity: number) {
const cookieStore = cookies();
const cartId = cookieStore.get('cartId')?.value;
if (!cartId) {
return null;
}
const { data } = await shopifyFetch(
`
mutation cartLinesUpdate($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
cartLinesUpdate(cartId: $cartId, lines: $lines) {
cart {
id
}
userErrors {
field
message
}
}
}
`,
{
cartId,
lines: [
{
id: lineId,
quantity,
},
],
}
);
revalidateTag('cart');
return data.cartLinesUpdate.cart;
}3. Multi-Layer Caching mit Webhook-Invalidation
// lib/shopify/cache.ts
import { unstable_cache } from 'next/cache';
// Multi-Layer Caching:
// 1. Next.js Data Cache (in-memory)
// 2. CDN Cache (Vercel Edge)
// 3. Shopify API Cache
export async function getCachedProducts() {
return unstable_cache(
async () => {
const products = await fetchProductsFromShopify();
return products;
},
['products'], // Cache-Key
{
revalidate: 3600, // ISR: 1 Stunde
tags: ['products'], // Tag für Invalidierung
}
)();
}
export async function getCachedProduct(handle: string) {
return unstable_cache(
async () => {
const product = await fetchProductFromShopify(handle);
return product;
},
['product', handle],
{
revalidate: 1800, // 30 Minuten
tags: [`product-${handle}`, 'products'],
}
)();
}
// Webhook Handler für Cache-Invalidierung
// app/api/webhooks/products/route.ts
import { revalidateTag } from 'next/cache';
import { verifyShopifyWebhook } from '@/lib/shopify/verify';
export async function POST(req: Request) {
const body = await req.text();
const hmac = req.headers.get('x-shopify-hmac-sha256');
// HMAC verifizieren
if (!verifyShopifyWebhook(body, hmac)) {
return new Response('Unauthorized', { status: 401 });
}
const product = JSON.parse(body);
// Cache für dieses spezifische Produkt invalidieren
revalidateTag(`product-${product.handle}`);
// Auch allgemeinen Products-Cache invalidieren
revalidateTag('products');
return new Response('OK', { status: 200 });
}Alternative: Remix
Remix ist eine moderne Alternative zu Next.js mit anderem Ansatz:
Vorteile von Remix:
- ✅ Nested Routes = bessere Code-Organisation
- ✅ Progressive Enhancement out-of-the-box
- ✅ Form-Handling mit Server Actions
- ✅ Simplere Datenladung (keine getServerSideProps/getStaticProps)
Nachteil: Kleinere Community als Next.js, weniger Shopify-Beispiele
Laufende monatliche Kosten
Jährliche Wartungskosten
Technische Wartung
Feature-Entwicklung
Total Cost of Ownership (3 Jahre)
Beispiel: Mittelgroßer Online-Shop
Was Sie NICHT selbst bauen sollten
1. Checkout
Verwenden Sie den Shopify Checkout – nicht Ihren eigenen!
Gründe:
- ✅ PCI-Compliant (keine Security-Risiken)
- ✅ Shopify Pay, Apple Pay, Google Pay included
- ✅ Fraud-Detection integriert
- ✅ 3D-Secure automatisch
- ✅ Shop Pay Installments (Buy Now, Pay Later)
Achtung: Custom Checkouts sind nur auf Shopify Plus erlaubt und sehr aufwändig.
// components/CartSummary.tsx
'use client';
import { useCart } from '@/hooks/useCart';
export function CartSummary() {
const { cart } = useCart();
if (!cart) return null;
const handleCheckout = () => {
// Zu Shopify Checkout weiterleiten
window.location.href = cart.checkoutUrl;
};
return (
<div className="bg-card p-6 rounded-xl">
<h3 className="text-lg font-bold mb-4">Warenkorb-Zusammenfassung</h3>
<div className="space-y-2 mb-4">
<div className="flex justify-between">
<span>Zwischensumme</span>
<span>
{cart.cost.subtotalAmount.amount} {cart.cost.subtotalAmount.currencyCode}
</span>
</div>
<div className="flex justify-between text-sm text-muted-foreground">
<span>Versand & Steuern</span>
<span>Bei Checkout berechnet</span>
</div>
</div>
<button
onClick={handleCheckout}
className="w-full bg-primary text-primary-foreground py-3 rounded-lg font-medium hover:bg-primary/90 transition"
>
Zur Kasse
</button>
</div>
);
}2. Payment Gateway Integration
Shopify kümmert sich um Stripe, PayPal & Co. – nutzen Sie das!
3. Inventory Management
Shopify Admin bleibt die Single Source of Truth für:
- Bestandsverwaltung
- Order-Management
- Fulfillment
Performance: Was Sie erwarten können
Klassisches Shopify Theme
Headless (Next.js optimiert)
Real-World-Beispiel: Theme → Headless Migration
Häufige Kostenfallen & wie Sie sie vermeiden
1. Unterschätzte Komplexität
Problem: "Ist doch nur ein Frontend" – falsch!
Lösung: Planen Sie 20% Buffer für:
- Unerwartete API-Limits
- Shopify-Updates
- Cross-Browser-Bugs
- Mobile Safari Quirks
2. Laufende Wartung vergessen
Problem: Nach Launch endet der Support – Shop läuft nicht mehr.
Lösung: Budgetieren Sie min. €500-€1.000/Monat für Wartung.
3. SEO-Anforderungen ignorieren
Problem: Headless-Shop ohne SEO = keine Google-Rankings.
Lösung: Von Tag 1:
- Server-Side Rendering (SSR)
- Structured Data (JSON-LD)
- Sitemap & robots.txt
- Meta-Tags für alle Seiten
// components/StructuredData.tsx
export function ProductStructuredData({ product }) {
const structuredData = {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.title,
description: product.description,
image: product.featuredImage.url,
brand: {
'@type': 'Brand',
name: product.vendor,
},
offers: {
'@type': 'Offer',
price: product.priceRange.minVariantPrice.amount,
priceCurrency: product.priceRange.minVariantPrice.currencyCode,
availability:
product.availableForSale
? 'https://schema.org/InStock'
: 'https://schema.org/OutOfStock',
url: `https://your-store.com/products/${product.handle}`,
},
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
/>
);
}4. Keine Staging-Umgebung
Problem: Direkt auf Production deployen = Ausfälle.
Lösung: Vercel bietet kostenlose Preview-Deployments pro Pull Request.
5. Image-Optimierung vergessen
Problem: 4000x4000px PNG-Bilder = langsamer Shop.
Lösung: Nutzen Sie next/image mit automatischer Kompression & WebP/AVIF.
// components/OptimizedImage.tsx
import Image from 'next/image';
export function ProductImage({ src, alt }: { src: string; alt: string }) {
return (
<Image
src={src}
alt={alt}
width={800}
height={800}
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
quality={85}
priority={false} // lazy loading
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRg..." // optional
className="w-full h-auto"
/>
);
}Wann lohnt sich Headless wirklich?
✅ Headless macht Sinn, wenn:
- Sie > €1 Mio. Jahresumsatz machen (ROI ist nachweisbar)
- Design-Freiheit geschäftskritisch ist
- Sie Multi-Channel verkaufen (Web + App + POS)
- Performance = Wettbewerbsvorteil (Fashion, Electronics)
- Ihr Team Frontend-Entwickler hat
- Sie internationale Märkte bedienen (Markets, Localization)
❌ Headless macht KEINEN Sinn, wenn:
- Budget < €15.000
- Sie in < 8 Wochen launchen müssen
- Ein Shopify Theme Ihre Anforderungen erfüllt
- Ihr Team keine Entwickler hat (nur Shopify-Designer)
- Wartungs-Budget nicht vorhanden
Fazit: Was kostet Headless wirklich?
Minimum Viable Headless Shop: €27.000 - €40.000 (Initial) + €10.000/Jahr (Betrieb)
Enterprise Headless Shop: €50.000 - €150.000 (Initial) + €30.000/Jahr (Betrieb)
ROI-Breakeven: Typischerweise nach 6-12 Monaten (durch bessere Conversion + Performance)
Sie überlegen, auf Headless zu wechseln?
In einem kostenlosen Erstgespräch analysiere ich:
- Ob Headless für Ihr Geschäftsmodell Sinn macht
- Realistische Kosten für Ihr spezifisches Projekt
- Tech-Stack-Empfehlungen (Next.js, Remix, Astro?)
- ROI-Kalkulation basierend auf Ihrem Traffic
- Performance-Potenzial Ihrer aktuellen Lösung
Headless Commerce für Ihr Business?
Lassen Sie uns Ihre Anforderungen besprechen und herausfinden, ob Headless die richtige Strategie ist.
Kostenloses Erstgespräch
Über den Autor: Justin Kreutzmann ist Spezialist für Shopify Headless Commerce mit Next.js. Er hat 20+ Headless-Projekte umgesetzt und berät Unternehmen bei der Entscheidung Theme vs. Headless.