TL;DR
Het Shopify theme.liquid-bestand bepaalt de volledige <head>-sectie van elke pagina — en daarmee ook hoe zoekmachines je hele shop zien. Ik heb 10 optimalisaties verzameld die een Dawn-theme van een 65 Lighthouse-score naar 90+ kunnen brengen. Inclusief copy-paste Liquid-code, uitleg van wat het doet en wat er mis kan gaan. Totale implementatietijd: 2-4 uur.
Ik heb via SEOJuice honderden Shopify-shops geaudit. Het patroon is bijna altijd hetzelfde: de eigenaar kiest een theme, past de kleuren aan, uploadt producten en raakt theme.liquid daarna nooit meer aan. Ondertussen rankt een concurrent met exact dezelfde producten en de helft van de backlinks erboven, simpelweg omdat hun developer een middag heeft besteed aan het optimaliseren van de themecode.
Het Shopify Dawn-theme scoort standaard 92 op PageSpeed — het snelste gratis Shopify-theme dat beschikbaar is. Maar “standaard” laat nog steeds flinke SEO-kansen liggen. Ontbrekende structured data. Niet-geoptimaliseerd laden van lettertypen. Geen preconnect-hints. Onvolledige OG-tags.
Deze gids lost dat allemaal op. En de volgende sectie — canonical URL's — is precies het onderdeel waarvan ik wou dat elke Shopify-shop-eigenaar het begreep voordat ze me mailden met de vraag waarom Google de verkeerde versie van hun productpagina’s indexeerde.
Als je geen developer bent: hier is de uitleg in 30 seconden. theme.liquid is het hoofdtemplatebestand dat om elke afzonderlijke pagina in je Shopify-shop heen zit. Het bepaalt de structuur van <html>, <head> en <body>. Elke productpagina, collectiepagina, blogpost en homepage is op dit bestand gebaseerd.
Als je iets toevoegt aan de <head>-sectie van theme.liquid, verschijnt het op elke pagina. Dat maakt het de perfecte plek voor:
Om het te bewerken: Shopify Admin → Online Store → Themes → Edit Code → Layout → theme.liquid
Belangrijkste punt
Maak altijd eerst een duplicaat van je theme voordat je iets aanpast. Ga naar Online Store → Themes → Actions → Duplicate. Werk in de kopie. Als er iets stukgaat, kun je direct terugschakelen.
Dit is wat we gaan implementeren, waar het hoort en wat het je oplevert:
| Optimalisatie | Waar toevoegen | SEO-impact | Moeilijkheid |
|---|---|---|---|
| Canonical URL's | theme.liquid <head> | Hoog — voorkomt duplicate content | Makkelijk |
| Organization-schema | theme.liquid <head> | Gemiddeld — helpt Google het knowledge panel van je merk op te bouwen | Makkelijk |
| Product-schema | product.liquid of section | Hoog — rich results in SERPs | Gemiddeld |
| Breadcrumb-schema | Snippet + template | Gemiddeld — breadcrumbs in de SERP | Gemiddeld |
| Fonts preloaden | theme.liquid <head> | Hoog — LCP-verbetering | Makkelijk |
| Critical CSS inlinen | theme.liquid <head> | Hoog — haalt render-blocking weg | Moeilijk |
| Niet-kritische JS uitstellen | theme.liquid <body> einde | Hoog — snellere DOM-verwerking | Gemiddeld |
| OG/Twitter meta tags | theme.liquid <head> | Gemiddeld — beter delen op social media | Makkelijk |
| Hreflang-tags | theme.liquid <head> | Hoog — internationale SEO | Gemiddeld |
| Afbeeldingen lazy loaden | Templates/sections | Gemiddeld — verlaagt initiële payload | Makkelijk |
Shopify genereert veel dubbele URL’s. Een product kan bereikbaar zijn via /products/widget en /collections/sale/products/widget — hetzelfde product, twee URL’s. Zonder canonical URL ziet Google duplicate content en kan het de verkeerde versie indexeren. Ik heb shops gezien waar Google voor elk afzonderlijk product de collectie-URL indexeerde, waardoor de “echte” product-URL’s nul autoriteit opbouwden.
Dawn bevat basisafhandeling voor canonical URL's, maar het is de moeite waard om dit te controleren en te verbeteren. Voeg dit toe aan je <head>-sectie in theme.liquid:
{%- liquid
assign canonical_url = canonical_url | default: request.origin | append: request.path
-%}
<link rel="canonical" href="{{ canonical_url }}">
{%- if paginate and paginate.pages > 1 -%}
{%- if paginate.previous -%}
<link rel="prev" href="{{ paginate.previous.url }}">
{%- endif -%}
{%- if paginate.next -%}
<link rel="next" href="{{ paginate.next.url }}">
{%- endif -%}
{%- endif -%}
Dit doet drie dingen: het zet de canonical URL met Shopify’s ingebouwde canonical_url-variabele (die de juiste URL voor producten oplost, ongeacht hoe ze worden benaderd), het voegt rel="prev"/rel="next" toe voor gepagineerde collecties, en het gebruikt anders het domein plus pad van de aanvraag als canonical_url niet beschikbaar is.
Belangrijkste punt
Controleer eerst of je theme niet al een canonical tag genereert voordat je dit toevoegt. Dubbele canonical tags verwarren zoekmachines meer dan helemaal geen canonical tag. Zoek eerst op “canonical” in je theme.liquid-bestand.
Organization structured data vertelt Google wie je bent — je bedrijfsnaam, logo, social-profielen en contactinformatie. Dit helpt Google het knowledge panel van je merk op te bouwen dat verschijnt wanneer iemand op je merknaam zoekt. Ik heb shops zien gaan van geen knowledge panel naar een volledig gevuld paneel binnen drie weken nadat deze markup correct was toegevoegd.
Voeg dit toe binnen <head> in theme.liquid. Het draait op elke pagina, en dat is precies wat Google wil:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": {{ shop.name | json }},
"url": "{{ shop.url }}",
{%- if shop.brand.logo -%}
"logo": {{ shop.brand.logo | image_url: width: 600 | json }},
{%- endif -%}
"description": {{ shop.description | json }},
"address": {
"@type": "PostalAddress",
"addressCountry": {{ shop.address.country | json }}
}
{%- if shop.brand.social_links.size > 0 -%}
,"sameAs": [
{%- for link in shop.brand.social_links -%}
{{ link | json }}{%- unless forloop.last -%},{%- endunless -%}
{%- endfor -%}
]
{%- endif -%}
}
</script>
Let erop dat ik de | json-filter gebruik in plaats van | escape. Dat is belangrijk — json formatteert strings correct voor JSON-LD, inclusief de aanhalingstekens eromheen. escape gebruiken in JSON-LD is een klassieke fout die ongeldige markup oplevert. Ik heb dit specifieke probleem op minstens een dozijn klantshops moeten debuggen — de structured data valideert in Google’s testtool, maar faalt stilletjes in productie omdat de escaping verkeerd is.
“JSON-LD is Google’s voorkeursformaat voor structured data. Als je schema markup implementeert op Shopify, kies dan altijd JSON-LD boven microdata — het is schoner, makkelijker te debuggen en vermengt zich niet met je presentatie-HTML.”
Product structured data is wat je die rich results in Google oplevert — sterrenbeoordelingen, prijs, beschikbaarheid. Dawn bevat basis product schema, maar die is vaak incompleet. Hieronder staat een uitgebreidere versie die ik heb aangescherpt door tests op tientallen shops.
Maak een nieuwe snippet aan: snippets/json-ld-product.liquid
{%- if product -%}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": {{ product.title | json }},
"url": "{{ shop.url }}{{ product.url }}",
"description": {{ product.description | strip_html | truncate: 500 | json }},
{%- if product.featured_image -%}
"image": [
{{ product.featured_image | image_url: width: 1200 | json }}
{%- for image in product.images offset: 1 limit: 4 -%}
,{{ image | image_url: width: 1200 | json }}
{%- endfor -%}
],
{%- endif -%}
"brand": {
"@type": "Brand",
"name": {{ product.vendor | json }}
},
{%- if product.selected_or_first_available_variant.sku != blank -%}
"sku": {{ product.selected_or_first_available_variant.sku | json }},
{%- endif -%}
{%- if product.selected_or_first_available_variant.barcode != blank -%}
"gtin": {{ product.selected_or_first_available_variant.barcode | json }},
{%- endif -%}
"offers": {
"@type": "AggregateOffer",
"priceCurrency": {{ cart.currency.iso_code | json }},
"lowPrice": {{ product.price_min | money_without_currency | json }},
"highPrice": {{ product.price_max | money_without_currency | json }},
"offerCount": {{ product.variants.size }},
"availability": "https://schema.org/{% if product.available %}InStock{% else %}OutOfStock{% endif %}",
"url": "{{ shop.url }}{{ product.url }}"
}
{%- if product.metafields.reviews.rating.value != blank -%}
,"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": {{ product.metafields.reviews.rating.value | json }},
"reviewCount": {{ product.metafields.reviews.rating_count.value | json }}
}
{%- endif -%}
}
</script>
{%- endif -%}
Voeg hem daarna toe in je producttemplate (meestal sections/main-product.liquid of templates/product.liquid):
{% render 'json-ld-product' %}
Belangrijke details: ik gebruik AggregateOffer in plaats van losse Offer-objecten, omdat dat netter is voor producten met meerdere varianten. De ratingvelden gebruiken Shopify’s native product review-metafields — als je een third-party review-app gebruikt, moet je de metafield namespace aanpassen. (Judge.me en Loox gebruiken verschillende namespaces — check hun docs voordat je dit blind kopieert.)
Breadcrumbs helpen Google je sitestructuur te begrijpen en kunnen direct in zoekresultaten verschijnen, in plaats van de ruwe URL. Dat is vooral waardevol voor shops met diepe categorie-structuren. Bij een shop die ik auditte veranderde breadcrumb structured data de SERP-weergave van example.com › collections › summer-2025 › products › red-dress naar een nette Home > Summer Collection > Red Dress — wat de click-through rate meetbaar verbeterde.
Maak snippets/json-ld-breadcrumbs.liquid aan:
{%- assign breadcrumb_list = "" | split: "" -%}
{%- if template.name == 'product' -%}
{%- if product.collections.size > 0 -%}
{%- assign primary_collection = product.collections | first -%}
{%- capture crumb_collection -%}{"name":{{ primary_collection.title | json }},"url":"{{ shop.url }}{{ primary_collection.url }}"}{%- endcapture -%}
{%- assign breadcrumb_list = breadcrumb_list | push: crumb_collection -%}
{%- endif -%}
{%- capture crumb_product -%}{"name":{{ product.title | json }},"url":"{{ shop.url }}{{ product.url }}"}{%- endcapture -%}
{%- assign breadcrumb_list = breadcrumb_list | push: crumb_product -%}
{%- elsif template.name == 'collection' -%}
{%- capture crumb_coll -%}{"name":{{ collection.title | json }},"url":"{{ shop.url }}{{ collection.url }}"}{%- endcapture -%}
{%- assign breadcrumb_list = breadcrumb_list | push: crumb_coll -%}
{%- elsif template.name == 'article' -%}
{%- capture crumb_blog -%}{"name":{{ blog.title | json }},"url":"{{ shop.url }}{{ blog.url }}"}{%- endcapture -%}
{%- assign breadcrumb_list = breadcrumb_list | push: crumb_blog -%}
{%- capture crumb_article -%}{"name":{{ article.title | json }},"url":"{{ shop.url }}{{ article.url }}"}{%- endcapture -%}
{%- assign breadcrumb_list = breadcrumb_list | push: crumb_article -%}
{%- endif -%}
{%- if breadcrumb_list.size > 0 -%}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "{{ shop.url }}"
}
{%- for crumb_json in breadcrumb_list -%}
,{
"@type": "ListItem",
"position": {{ forloop.index | plus: 1 }},
"name": {{ crumb_json | split: '"name":' | last | split: ',"url"' | first }},
"item": {{ crumb_json | split: '"url":' | last | split: '}' | first }}
}
{%- endfor -%}
]
}
</script>
{%- endif -%}
Voeg {% render 'json-ld-breadcrumbs' %} toe in je theme.liquid vlak voor </head>. De snippet detecteert automatisch het paginatype en bouwt de juiste breadcrumb-keten op.
Deze volgende wijziging maakte het grootste verschil op onze testshops. Alleen al fonts preloaden kan een volle seconde van time-to-text afhalen — en voor e-commerce correleert die seconde direct met bounce rate. Shopify’s eigen engineeringteam liet het effect zien op shopify.com zelf:
“With preload, front-end developers can tell the browser to download the font files needed at the same time as the CSS files. Only 17 lines of code decreased the time to display text on shopify.com by 50%."
Het probleem: browsers kunnen fonts pas downloaden nadat ze zowel de HTML als de CSS volledig hebben verwerkt die naar die fonts verwijst. Dat zijn twee opeenvolgende round trips voordat tekst verschijnt. Preloading vertelt de browser: “begin dit font nu alvast te downloaden, je hebt het zo nodig.”
Voeg dit hoog in de <head> van theme.liquid toe, vóór je stylesheet-tags:
{%- comment -%}
Preload your primary heading and body fonts.
Check your theme's font settings to get the correct filenames.
Use font-display: swap in your CSS to prevent FOIT (Flash of Invisible Text).
{%- endcomment -%}
{%- if settings.type_header_font != blank -%}
<link rel="preload"
href="{{ settings.type_header_font | font_url }}"
as="font"
type="font/woff2"
crossorigin>
{%- endif -%}
{%- if settings.type_body_font != blank -%}
<link rel="preload"
href="{{ settings.type_body_font | font_url }}"
as="font"
type="font/woff2"
crossorigin>
{%- endif -%}
{%- comment -%} Preconnect to Shopify's CDN for faster asset loading {%- endcomment -%}
<link rel="preconnect" href="https://cdn.shopify.com" crossorigin>
<link rel="preconnect" href="https://fonts.shopifycdn.com" crossorigin>
Het attribuut crossorigin is verplicht bij font preloads — zonder dat downloadt de browser het font twee keer. Ik heb precies deze fout op productieshops gezien, waarbij alleen al het verwijderen van die dubbele download de LCP met 400ms verbeterde. De preconnect-hints besparen nog eens ongeveer ~100ms door de TCP + TLS-verbinding met Shopify’s CDN vroegtijdig op te zetten.
Belangrijkste punt
Preload alleen fonts die je boven de vouw gebruikt. Elke preload concurreert om bandbreedte. Als je vijf fonts preloadt, heb je het doel om zeep geholpen — de browser gaat ze alle vijf parallel downloaden en vertraagt daarmee de rest.
Render-blocking CSS is de #1 reden waarom Shopify-shops slecht scoren in Lighthouse. De browser kan niets tekenen totdat hij je volledige stylesheet heeft gedownload en verwerkt — zelfs de CSS voor de footer die 3,000px onder de vouw staat. Dit is de lastigste optimalisatie op deze lijst, maar ook degene die in audits het vaakst de grootste Lighthouse-winst oplevert.
De oplossing: inline de CSS die nodig is voor content boven de vouw direct in <head>, en laad de rest asynchroon.
{%- comment -%}
Step 1: Inline critical CSS directly in the head.
Generate your critical CSS using a tool like our
Critical CSS Generator at /tools/critical-css-generator/
Then paste it into a snippet: snippets/critical-css.liquid
{%- endcomment -%}
<style>
{% render 'critical-css' %}
</style>
{%- comment -%}
Step 2: Load full stylesheet asynchronously using the media trick.
The browser treats media="print" as non-render-blocking,
then onload switches it to media="all" so styles apply.
{%- endcomment -%}
<link rel="stylesheet"
href="{{ 'base.css' | asset_url }}"
media="print"
onload="this.media='all'">
<noscript>
<link rel="stylesheet" href="{{ 'base.css' | asset_url }}">
</noscript>
Als alternatief biedt Shopify de inline_asset_content Liquid-filter, waarmee je CSS direct uit je assetbestanden kunt inlinen, zonder aparte snippet:
<style>{{ 'critical.css' | asset_url | inline_asset_content }}</style>
Critical CSS correct genereren vereist analyse van elk template. Ik raad aan om een critical CSS generator tool te gebruiken — die haalt precies de CSS-regels eruit die nodig zijn voor content boven de vouw per paginatype. Als je dit verkeerd doet (bijvoorbeeld een ontbrekende regel voor je hero-sectie), krijg je een flash of unstyled content die erger is dan helemaal niet optimaliseren.
Parser-blocking scripts zijn na render-blocking CSS de grootste snelheidskiller. Shopify’s officiële advies is duidelijk: je geminificeerde JS-bundle moet 16KB of minder zijn, en alles daarbuiten moet worden uitgesteld.
Zoek in theme.liquid je <script>-tags op en voeg defer toe:
{%- comment -%}
WRONG — blocks DOM parsing:
{{ 'theme.js' | asset_url | script_tag }}
RIGHT — deferred loading:
{%- endcomment -%}
<script src="{{ 'theme.js' | asset_url }}" defer></script>
{%- comment -%}
For third-party scripts (analytics, chat widgets, etc.),
load them after the page is interactive:
{%- endcomment -%}
<script>
// Load non-essential scripts after user interaction
function loadDeferredScripts() {
// Example: chat widget, analytics, etc.
var scripts = [
'{{ "deferred-features.js" | asset_url }}'
];
scripts.forEach(function(src) {
var s = document.createElement('script');
s.src = src;
s.defer = true;
document.body.appendChild(s);
});
}
// Trigger on first user interaction
['click', 'scroll', 'keydown', 'touchstart'].forEach(function(evt) {
window.addEventListener(evt, function handler() {
loadDeferredScripts();
['click', 'scroll', 'keydown', 'touchstart'].forEach(function(e) {
window.removeEventListener(e, handler);
});
}, { once: true, passive: true });
});
</script>
Dit interaction-based loading-patroon is agressief, maar effectief. Chatwidgets, review-popups en vergelijkbare features hoeven niet te laden totdat de gebruiker daadwerkelijk iets doet op de pagina. Bij een shop die we testten haalde dit patroon 340ms van Time to Interactive af — precies het verschil tussen een “needs improvement” en een “good” INP-score in CrUX-data.
Wanneer iemand je product deelt op Facebook, Twitter of LinkedIn, bepalen OG-tags (Open Graph) wat er in de preview verschijnt. Zonder die tags gokken social platforms — en ze gokken vaak dramatisch slecht. Ik heb productshares gezien met de favicon van de shop als afbeelding en de titel van de checkoutpagina als beschrijving.
Dawn bevat een social-meta-tags.liquid-snippet, maar die is vaak minimaal. Hier is een uitgebreidere versie. Maak snippets/social-meta-tags.liquid aan of werk hem bij:
{%- liquid
assign og_title = page_title | default: shop.name
assign og_description = page_description | default: shop.description | strip_html | truncate: 200
assign og_url = canonical_url | default: request.origin | append: request.path
assign og_type = 'website'
assign og_image = ''
-%}
{%- if template.name == 'product' -%}
{%- assign og_type = 'product' -%}
{%- if product.featured_image -%}
{%- assign og_image = product.featured_image | image_url: width: 1200, height: 630 -%}
{%- endif -%}
{%- elsif template.name == 'article' -%}
{%- assign og_type = 'article' -%}
{%- if article.image -%}
{%- assign og_image = article.image | image_url: width: 1200, height: 630 -%}
{%- endif -%}
{%- elsif template.name == 'collection' -%}
{%- if collection.image -%}
{%- assign og_image = collection.image | image_url: width: 1200, height: 630 -%}
{%- endif -%}
{%- endif -%}
{%- if og_image == blank and shop.brand.cover_image -%}
{%- assign og_image = shop.brand.cover_image | image_url: width: 1200, height: 630 -%}
{%- endif -%}
<!-- Open Graph -->
<meta property="og:site_name" content="{{ shop.name }}">
<meta property="og:title" content="{{ og_title }}">
<meta property="og:description" content="{{ og_description }}">
<meta property="og:url" content="{{ og_url }}">
<meta property="og:type" content="{{ og_type }}">
{%- if og_image != blank -%}
<meta property="og:image" content="{{ og_image }}">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
{%- endif -%}
{%- if template.name == 'product' -%}
<meta property="product:price:amount" content="{{ product.price | money_without_currency }}">
<meta property="product:price:currency" content="{{ cart.currency.iso_code }}">
{%- endif -%}
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ og_title }}">
<meta name="twitter:description" content="{{ og_description }}">
{%- if og_image != blank -%}
<meta name="twitter:image" content="{{ og_image }}">
{%- endif -%}
Voeg dit in theme.liquid toe met {% render 'social-meta-tags' %} binnen <head>. De afbeeldingsafmetingen van 1200x630 zijn geoptimaliseerd voor zowel Facebook als het large card-formaat van Twitter/X.
Als je in meerdere landen of talen verkoopt, vertellen hreflang-tags aan Google welke versie van een pagina aan welk publiek moet worden getoond. Zonder die tags kan je Franse shop in Duitse zoekresultaten verschijnen — en ik heb dit letterlijk zien gebeuren bij een klant die maandenlang supporttickets in de verkeerde taal kreeg voordat de oorzaak duidelijk werd.
Shopify Markets genereert automatisch hreflang-tags voor internationale domeinen en subfolders. Maar als je meer controle nodig hebt — of een vertaalapp gebruikt — voeg dan dit toe aan de <head> van theme.liquid:
{%- if request.locale and localization.available_languages.size > 1 -%}
{%- for locale in localization.available_languages -%}
{%- if locale.primary -%}
{%- assign locale_root = shop.url -%}
{%- else -%}
{%- assign locale_root = shop.url | append: '/' | append: locale.iso_code -%}
{%- endif -%}
<link rel="alternate"
hreflang="{{ locale.iso_code }}"
href="{{ locale_root }}{{ request.path }}">
{%- endfor -%}
{%- comment -%} x-default points to your primary language {%- endcomment -%}
<link rel="alternate"
hreflang="x-default"
href="{{ shop.url }}{{ request.path }}">
{%- endif -%}
De x-default-tag is cruciaal — die vertelt Google welke versie moet worden getoond als geen van de opgegeven talen overeenkomt met de voorkeur van de gebruiker. Laat die altijd verwijzen naar je primaire markt.
Native lazy loading wordt inmiddels ondersteund door 92% van de browsers. Shopify’s image_tag Liquid-filter handelt dit automatisch af — afbeeldingen na de eerste drie secties krijgen standaard loading="lazy".
Maar je moet er wel voor zorgen dat je afbeeldingen boven de vouw niet lazy loadt. Dat is een veelgemaakte fout die LCP juist verslechtert. Vorige maand auditte ik een shop met loading="lazy" op de hero-banner — dat voegde 800ms toe aan hun LCP omdat de browser de belangrijkste afbeelding op de pagina lagere prioriteit gaf:
{%- comment -%}
Hero image — above the fold, NEVER lazy load.
Use loading: 'eager' and fetchpriority: 'high' to prioritize it.
{%- endcomment -%}
{{ section.settings.hero_image
| image_url: width: 1920
| image_tag:
loading: 'eager',
fetchpriority: 'high',
sizes: '100vw',
widths: '375, 750, 1100, 1500, 1920'
}}
{%- comment -%}
Product images below the fold — let Shopify's default lazy loading work.
Don't set loading attribute; Shopify handles it via section.index.
{%- endcomment -%}
{{ product.featured_image
| image_url: width: 800
| image_tag:
sizes: '(min-width: 750px) 50vw, 100vw',
widths: '375, 750, 800'
}}
Het attribuut fetchpriority: 'high' op je LCP-afbeelding is een relatief nieuwe toevoeging die de browser vertelt deze afbeelding voorrang te geven boven andere resources. Gecombineerd met het feit dat je hem niet lazy loadt, is dit waarschijnlijk de meest impactvolle wijziging die je kunt doen voor je LCP-score.

Je hebt de wijzigingen doorgevoerd. Controleer nu of ze ook echt werken. (Ik heb developers deze stap zien overslaan en pas drie maanden later zien ontdekken dat hun structured data al die tijd ongeldig was.)
Stap 1: Valideer structured data. Ga naar Google's Rich Results Test en voer je product-URL in. Elk type structured data dat je hebt toegevoegd, moet verschijnen met een groen vinkje. Veelvoorkomende fouten: ontbrekende komma’s in JSON-LD-arrays, niet-geëscape aanhalingstekens in productbeschrijvingen en ongeldige afbeeldings-URL’s.
Stap 2: Controleer canonical URL's. Bekijk de paginabron (Ctrl+U) en zoek op “canonical”. Je zou precies één canonical tag per pagina moeten zien. Zie je er twee, dan voegde je theme er al één toe — verwijder de dubbele.
Stap 3: Draai Lighthouse. Open Chrome DevTools → Lighthouse → vink Performance en SEO aan → Generate Report. Vergelijk je scores van vóór en na de wijzigingen. Bewaar screenshots — je wilt die voor/na-vergelijking hebben wanneer een klant vraagt wat je precies hebt gedaan.
Stap 4: Test social previews. Gebruik Facebook's Sharing Debugger en Twitter's Card Validator om te bekijken hoe je productpagina’s eruitzien wanneer ze gedeeld worden.
Veelgemaakte fouten die je later pijn gaan doen:

Dit is wat je realistisch kunt verwachten van elke optimalisatie, gebaseerd op audits die ik heb uitgevoerd op Dawn-gebaseerde shops:
| Optimalisatie | Verwachte LCP-verandering | Verwachte CLS-verandering | Lighthouse-punten |
|---|---|---|---|
| Fonts preloaden | -300ms to -1.2s | Geen verandering | +3 to +8 |
| Critical CSS inlinen | -200ms to -800ms | -0.02 to -0.05 | +5 to +15 |
| Niet-kritische JS uitstellen | -100ms to -500ms | Geen verandering | +3 to +10 |
| Afbeeldingen lazy loaden (correct) | -100ms to -400ms | Lichte verbetering | +2 to +5 |
| Structured data (alle types) | Geen verandering | Geen verandering | +2 to +5 (SEO-score) |
| Canonical URL's + hreflang | Geen verandering | Geen verandering | +1 to +3 (SEO-score) |
| Gecombineerd totaal | -0.7s to -2.9s | -0.02 to -0.05 | +15 to +35 |
Deze cijfers verschillen flink afhankelijk van je startpunt. Een shop die al 85+ scoort in Lighthouse zal kleinere winsten zien dan een shop die op 55 zit. Fonts preloaden en critical CSS leveren consequent de grootste verbeteringen op — dus als je weinig tijd hebt, begin dan met die twee.
Als je de themecode direct bewerkt — nee. Theme-updates overschrijven je wijzigingen. Daarom raad ik aan om waar mogelijk snippets te gebruiken (json-ld-product.liquid, social-meta-tags.liquid, enzovoort). Snippets overleven sommige updates. Voor maximale veiligheid: documenteer elke wijziging die je maakt en pas ze opnieuw toe na updates, of gebruik een version control-workflow met Shopify CLI.
Ja, apps zoals JSON-LD for SEO en Smart SEO regelen structured data zonder codewijzigingen. De afruil: apps voegen JavaScript-overhead toe (ironisch voor snelheidsoptimalisatie), kosten $5-20/maand en geven je minder controle. Voor fonts preloaden en critical CSS is er geen echte vervanging voor code-level wijzigingen.
De structured data, OG-tags en hreflang-code werken met elk Shopify-theme. De syntax voor fonts preloaden kan verschillen — check de settings_schema.json van je theme voor de juiste font-variabelen. Critical CSS is per definitie theme-specifiek. De Liquid-codepatronen zijn universeel binnen Online Store 2.0-themes.
Gebruik Google’s Rich Results Test (search.google.com/test/rich-results) voor individuele URL’s. Voor validatie op siteniveau kijk je in Google Search Console → Enhancements. Het kan 2-4 weken duren voordat Google nieuwe structured data verwerkt en resultaten toont in het Enhancements-rapport.
defer en async op script-tags?defer downloadt het script parallel, maar wacht met uitvoeren totdat de HTML volledig is verwerkt. async downloadt ook parallel en voert het script meteen uit zodra het klaar is — wat het parseren van de HTML kan onderbreken. Gebruik voor theme-scripts altijd defer. Gebruik async alleen voor echt onafhankelijke scripts zoals analytics die niet met de DOM interageren.
Deze 10 optimalisaties zijn de wijzigingen met de grootste impact die je in theme.liquid kunt doen. Maar SEO is meer dan code. Je productbeschrijvingen, interne links, alt-teksten van afbeeldingen en contentstrategie zijn minstens zo belangrijk.
Als je dieper wilt gaan:
theme.liquidno credit card required
No related articles found.