smplx.
Shopify Architektur

Performance-Architektur für Shopify: Schnelle Shops bauen [2026]

Claudio Gerlich··14 min

Im Shopify-Architektur-Ratgeber sprechen wir über Grundlagen. Heute beschäftigen wir uns mit einer Tatsache, die viele ignorieren: Performance ist Architektur.

Bei smplx. haben wir mit J.Clay gearbeitet und eine +107% Revenue-Steigerung durch Performance-Optimierungen erreicht. Das war nicht Zufall – es war systematische Architektur-Arbeit.

Diese Lektion möchten wir mit dir teilen.

Warum Performance Architektur ist

Die These: Schnelle Shops sind nicht das Ergebnis von schnellen Server. Sie sind das Ergebnis von durchdachten Architektur-Entscheidungen.

Die Zahlen:

  • Jede 100ms Verzögerung = 0.7% weniger Conversions (Google)
  • 53% verlassen Shops, wenn sie über 3 Sekunden dauern
  • Mobile Nutzer sind 2x sensibler auf Slow-Motion

Aber hier ist die wichtigere Wahrheit: Diese Zahlen entstehen nicht durch schnellere Server. Sie entstehen durch:

  1. Was du lädst
  2. Wann du es lädst
  3. Ob der Browser es cachen kann

Das ist Architektur, nicht DevOps.

Core Web Vitals: Die Metriken, die zählen

Google misst drei Metriken:

1. LCP (Largest Contentful Paint)

Was ist das? Wann ist die größte sichtbare Inhalt geladen und sichtbar?

Benchmark:

  • Gut: < 2.5s
  • Akzeptabel: 2.5-4.0s
  • Schlecht: > 4.0s

Was beeinflusst LCP?

  • Bilder (die große Hero-Grafik)
  • Text (wenn nicht in den HTML eingebettet)
  • Videos
  • Background-Images mit CSS

2. FID (First Input Delay) / INP (Interaction to Next Paint)

Was ist das? Wie lange dauert es, bis der Browser auf deine Interaktion reagiert?

Benchmark:

  • Gut: < 100ms
  • Akzeptabel: 100-300ms
  • Schlecht: > 300ms

Was beeinflusst INP?

  • JavaScript-Execution (zu viel JS blockiert den Main Thread)
  • Long Tasks (mehr als 50ms JavaScript ohne Pause)
  • Fehlende Event-Listener

3. CLS (Cumulative Layout Shift)

Was ist das? Wackelt deine Seite während des Ladens?

Benchmark:

  • Gut: < 0.1
  • Akzeptabel: 0.1-0.25
  • Schlecht: > 0.25

Was beeinflusst CLS?

  • Bilder ohne Width/Height
  • Fonts, die sich ändern
  • Ads, die dazukommen
  • Cookie-Banner

Critical Rendering Path: Das Fundament

Was ist das? Die Reihenfolge, in der der Browser deine Seite rendert.

1. HTML parsen
2. CSS parsen (blockiert Rendering!)
3. JavaScript ausführen (blockiert Rendering!)
4. Erste Pixel rendern (LCP Start)
5. Interaktiv werden (INP relevanter)
6. Alles geladen (LCP Ende)

Die Architektur-Entscheidung: Minimiere Blockierungen.

Liquid und Critical CSS

<!-- ❌ FALSCH: Alles wird blockiert -->
<link rel="stylesheet" href="/cdn/all.css">
<script src="/cdn/all.js"></script>

<!-- ✅ RICHTIG: Kritisch inline, Rest async -->
<style>
  /* Nur Critical CSS: Header, Hero, Above-Fold */
  .header { ... }
  .hero { ... }
  .button { ... }
</style>

<link rel="stylesheet" href="/cdn/non-critical.css" media="print" onload="this.media='all'">

<script defer src="/cdn/main.js"></script>
<script async src="/cdn/analytics.js"></script>

Was das macht:

  • Kritisches CSS blockiert nicht
  • Non-Critical CSS lädt im Hintergrund
  • JavaScript lädt mit defer (nach HTML-Parse)
  • Analytics mit async (gar nicht kritisch)

Image Strategy: Der größte Performance-Hebel

Die Realität: Bilder sind 60-80% der Bandwidth auf Shopify-Shops.

Responsive Images mit Picture

<!-- ❌ FALSCH: Eine Größe für alle -->
<img src="{{ product.featured_image.src }}" alt="{{ product.featured_image.alt }}">

<!-- ✅ RICHTIG: WebP, mehrere Sizes -->
<picture>
  <source
    type="image/webp"
    srcset="{{ product.featured_image | img_url: '300x' }} 300w,
            {{ product.featured_image | img_url: '600x' }} 600w,
            {{ product.featured_image | img_url: '900x' }} 900w"
    sizes="(max-width: 768px) 100vw, 50vw">
  <img
    src="{{ product.featured_image | img_url: '600x' }}"
    alt="{{ product.featured_image.alt }}"
    width="600"
    height="400"
    loading="lazy"
    decoding="async">
</picture>

Was das bewirkt:

  • WebP für Browser, die es können
  • Responsive Größen basierend auf Device
  • loading="lazy" = nicht im Viewport wird später geladen
  • decoding="async" = blockiert nicht den Main Thread

Shopify's Image Delivery API

<!-- Mit Shopify's native Transformation -->
{{ product.featured_image | image_url: width: 600 }}

Shopify liefert Bilder vom CDN mit automatischen Transformationen. Das ist besser als externe Image-Optimizer.

Font Strategy: Oft übersehen

Fonts sind meist >50KB. Sie können LCP blockieren!

<!-- ❌ FALSCH: Google Fonts blockieren -->
<link href="https://fonts.googleapis.com/css2?family=Inter" rel="stylesheet">

<!-- ✅ RICHTIG: Preload + Swap -->
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Inter&display=swap" rel="stylesheet">

Oder noch besser: Self-Host deine Fonts.

<!-- ✅ NOCH BESSER: Self-Hosted -->
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter.woff2') format('woff2');
  font-display: swap;
}

Warum font-display: swap? Der Browser zeigt sofort die Fallback-Font, dann tauscht sie aus. Keine weiße LCP-Phase.

Third-Party Scripts: Die versteckte Kostenstelle

Die Realität: Ein durchschnittlicher Shopify-Shop lädt 30+ External Scripts.

Das ist nicht OK. Jeder externe Script:

  • Braucht einen DNS-Lookup
  • Braucht einen HTTP-Request
  • Lädt extra JavaScript
  • Konkurriert mit deinem JavaScript

Script-Audit machen

// In der Browser Console
performance.getEntriesByType('resource')
  .filter(r => r.initiatorType === 'script')
  .forEach(r => {
    console.log(r.name, Math.round(r.duration) + 'ms', r.transferSize);
  });

Was kommt raus?

  • Google Analytics: ~200ms
  • Facebook Pixel: ~300ms
  • Attentive (Popups): ~400ms
  • Custom Font Loader: ~150ms
  • ...
  • Total: 3+ Sekunden nur für Third-Party!

Script-Architektur

<!-- ❌ FALSCH: Alles sofort -->
<script src="analytics.js"></script>
<script src="pixel.js"></script>
<script src="popup.js"></script>
<script src="chat.js"></script>

<!-- ✅ RICHTIG: Lazy Loading -->
<script>
  // Analytics sofort (ist wichtig)
  // Aber mit async
  const script = document.createElement('script');
  script.src = '/analytics.js';
  script.async = true;
  document.head.appendChild(script);

  // Popup/Chat: Nur wenn interaktiv
  window.addEventListener('DOMContentLoaded', () => {
    loadScript('/popup.js');
  });

  // Third-party nach 5 Sekunden
  setTimeout(() => {
    loadScript('/pixel.js');
  }, 5000);
</script>

Das ist Architektur: Was ist kritisch? Was kann warten?

Caching: Der unterschätzte Hebel

HTTP-Caching für Theme-Assets

<!-- Im Theme liquid (z.B. theme.liquid) -->
<link rel="stylesheet" href="{{ 'theme.css' | asset_url }}" media="all">

Shopify serviert Assets mit langen Cache-Headers. Das ist gut. Aber:

  • Bilder: Ändert sich oft → kurzer Cache
  • CSS: Ändert sich selten → langer Cache
  • JavaScript: Ändert sich regelmäßig → mittlerer Cache

Browser Caching mit Liquid

{% if request.host == 'shop.example.com' %}
  <!-- Bereits cachiert, kann aggressiv sein -->
  <img src="{{ product.image | img_url: '300x' }}" loading="lazy">
{% endif %}

Service Worker für Offline

// Wenn du ein Headless Shop hast (Next.js/Remix)
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js');
}

// Im Service Worker
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open('v1').then((cache) => {
      return cache.addAll([
        '/',
        '/products',
        '/collections'
      ]);
    })
  );
});

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then((response) => response || fetch(event.request))
  );
});

Das erlaubt Offline-Navigation und schnellere Loads durch Caching.

Code-Splitting: Nur laden, was nötig ist

Liquid mit Conditional JavaScript

{% if page.handle == 'contact' %}
  <script src="{{ 'form-validation.js' | asset_url }}"></script>
{% endif %}

{% if template == 'product' %}
  <script src="{{ 'product-gallery.js' | asset_url }}"></script>
{% endif %}

Headless (Next.js) mit Dynamic Imports

// components/ProductGallery.jsx
import dynamic from 'next/dynamic';

const GalleryComponent = dynamic(
  () => import('./gallery-component'),
  { loading: () => <p>Lädt...</p> }
);

export default function ProductPage() {
  return (
    <>
      <ProductInfo />
      <GalleryComponent />
    </>
  );
}

Der Browser lädt gallery-component nur auf der Produktseite.

Lazy Loading Sections: Nur Above-Fold rendern

In Liquid/Traditional Theme

<!-- Above-Fold: Rendern -->
<section class="hero">
  ...
</section>

<!-- Below-Fold: Lazy-Load -->
<div
  class="lazy-section"
  data-src="{% section 'product-carousel' %}">
  Lädt später...
</div>

<script>
  // Intersection Observer: Nur laden wenn sichtbar
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const section = entry.target;
        const html = document.querySelector(`[data-src="${section.dataset.src}"]`).dataset.src;
        section.innerHTML = html;
        observer.unobserve(section);
      }
    });
  });

  document.querySelectorAll('.lazy-section').forEach(section => {
    observer.observe(section);
  });
</script>

Effekt: Eine 10-Section Seite wird nur mit 2-3 Sections initial geladen.

Fallstudie: J.Clay [+107% Revenue]

J.Clay brauchte Geschwindigkeit. Hier, was wir machten:

Vorher

  • LCP: 3.8s
  • FID: 280ms
  • CLS: 0.15
  • Konversion: 2.1%

Architektur-Änderungen

  1. Image Strategy (größter Impact)

    • WebP + Responsive Sizes
    • Lazy Loading für Below-Fold
    • Result: -1.2s LCP
  2. Third-Party Cleanup

    • Pixel entfernt (nicht ROI-positiv)
    • Analytics mit async
    • Popup-Script lazily loaded
    • Result: -0.6s LCP, -150ms FID
  3. Critical CSS Inline

    • Header + Hero + CTA inline
    • Rest mit media="print" trick
    • Result: -0.4s LCP
  4. Code Splitting

    • Kauf-Flow Script nur auf Checkout
    • Product Gallery nur auf Product Pages
    • Result: -100ms INP

Nachher

  • LCP: 1.1s
  • FID: 85ms
  • CLS: 0.08
  • Konversion: 4.3% (+107%!)

Das war nicht "schnellere Server". Das war Architektur.

Performance Monitoring: Nicht bauen und vergessen

Liquid-basiertes Tracking

<script>
  window.addEventListener('load', () => {
    const metrics = {
      lcp: performance.getEntriesByName('largest-contentful-paint')[0]?.renderTime,
      fid: performance.getEntriesByType('first-input')[0]?.processingStart,
      cls: Array.from(document.querySelectorAll('[style*="transform"]')).length
    };

    // An Analytics senden
    fetch('/api/metrics', {
      method: 'POST',
      body: JSON.stringify(metrics)
    });
  });
</script>

Mit Web Vitals Library (empfohlen)

import { getLCP, getFID, getCLS } from 'web-vitals';

getLCP(console.log);
getFID(console.log);
getCLS(console.log);

Checkliste: Performance-Architektur-Review

Nutze das für dein nächstes Projekt:

  • LCP unter 2.5s (Lighthouse)
  • Alle Bilder responsive + WebP
  • Keine Bilder ohne Width/Height
  • Kritisches CSS inline, Rest deferred
  • Fonts mit font-display: swap
  • Dritte Scripts nur wenn nötig, async/defer
  • Code-Splitting nach Template/Page
  • Service Worker für Offline (falls Headless)
  • Lazy Loading für Below-Fold Sections
  • Caching-Headers konfiguriert
  • Web Vitals Monitoring aktiv
  • Lighthouse Score > 85

Über den Autor

Claudio Gerlich ist Technical Architect bei smplx. und seit 2020 spezialisiert auf Shopify. Mit J.Clay haben wir bewiesen, dass +100% Revenue-Steigerung durch durchdachte Performance-Architektur möglich ist.

Performance ist nicht Glück. Es ist Entscheidungen. Gute Entscheidungen.

smplx. ist Shopify Technical Partner (seit 2020) mit Sitz in Coesfeld, NRW.