TL;DR
Plik theme.liquid w Shopify kontroluje sekcję <head> na każdej stronie — czyli de facto to, jak wyszukiwarki widzą cały twój sklep. Zebrałem 10 optymalizacji, które potrafią podnieść wynik Lighthouse dla motywu Dawn z 65 do 90+. Każda zawiera gotowy kod Liquid do wklejenia, wyjaśnienie, co robi, i na co uważać. Łączny czas wdrożenia: 2-4 godziny.
Przeaudytowałem setki sklepów Shopify przez SEOJuice. Schemat jest prawie zawsze ten sam: właściciel sklepu wybiera motyw, dopasowuje kolory, dodaje produkty i nigdy więcej nie zagląda do theme.liquid. W tym samym czasie konkurent z identycznym asortymentem i połową backlinków wyprzedza go w wynikach, bo jego programista poświęcił jedno popołudnie na optymalizację kodu motywu.
Motyw Shopify Dawn osiąga 92 w PageSpeed zaraz po wdrożeniu — to najszybszy darmowy motyw Shopify dostępny obecnie. Ale nawet w wersji domyślnej wciąż tracisz sporo łatwych okazji SEO. Brak danych strukturalnych. Nieoptymalne ładowanie krojów pisma. Brak wskazówek preconnect. Niepełne tagi OG.
Ten poradnik naprawia to wszystko. A kolejna sekcja — tagi canonical — to właśnie tę część powinien zrozumieć każdy właściciel sklepu Shopify, zanim napisze do mnie z pytaniem, czemu Google indeksuje złą wersję strony produktu.
Jeśli nie jesteś programistą, oto wersja w 30 sekund: theme.liquid to główny plik szablonu, który obejmuje wszystkie strony w twoim sklepie Shopify. Definiuje strukturę <html>, <head> i <body>. Każda strona produktu, kolekcji, wpis blogowy i strona główna dziedziczy po tym pliku.
Kiedy dodasz coś do sekcji <head> w theme.liquid, pojawi się to na każdej stronie. Dlatego to idealne miejsce na:
Aby go edytować: Shopify Admin → Online Store → Themes → Edit Code → Layout → theme.liquid
Najważniejszy wniosek
Zawsze duplikuj motyw przed edycją. Wejdź w Online Store → Themes → Actions → Duplicate. Pracuj na kopii. Jeśli coś przestanie działać, możesz natychmiast wrócić do poprzedniej wersji.
Oto co wdrażamy, gdzie to dodać i jaką da ci to korzyść:
| Optymalizacja | Gdzie dodać | Wpływ na SEO | Poziom trudności |
|---|---|---|---|
| Tagi canonical | theme.liquid <head> | Wysoki — zapobiega duplikacji treści | Łatwe |
| Dane strukturalne organizacji | theme.liquid <head> | Średni — panel wiedzy marki | Łatwe |
| Dane strukturalne produktu | product.liquid lub sekcja | Wysoki — wyniki rozszerzone w SERP | Średnie |
| Dane strukturalne okruszków nawigacyjnych | Snippet + szablon | Średni — okruszki nawigacyjne w SERP | Średnie |
| Preload krojów pisma | theme.liquid <head> | Wysoki — poprawa LCP | Łatwe |
| Osadzenie krytycznego CSS bezpośrednio w kodzie | theme.liquid <head> | Wysoki — eliminuje blokowanie renderowania | Trudne |
| Odroczone ładowanie niekrytycznego JavaScriptu | theme.liquid <body> na końcu | Wysoki — szybsze parsowanie DOM | Średnie |
| Tagi meta OG/Twitter | theme.liquid <head> | Średni — lepsze udostępnianie w mediach społecznościowych | Łatwe |
| Tagi hreflang | theme.liquid <head> | Wysoki — międzynarodowe SEO | Średnie |
| Lazy loading obrazów | Szablony/sekcje | Średni — zmniejsza początkowy rozmiar ładowanych zasobów | Łatwe |
Shopify generuje mnóstwo zduplikowanych URL-i. Produkt może być dostępny przez /products/widget i /collections/sale/products/widget — ten sam produkt, dwa adresy URL. Bez tagu canonical Google widzi zduplikowaną treść i może zaindeksować niewłaściwy adres. Widziałem sklepy, w których Google indeksował adres produktu ze ścieżką kolekcji dosłownie dla każdego produktu, przez co „prawdziwe” adresy produktów miały zerowy autorytet.
Dawn ma podstawową obsługę canonical, ale i tak warto to sprawdzić i dopracować. Dodaj to do sekcji <head> w 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 -%}
To robi trzy rzeczy: ustawia canonical URL przy użyciu wbudowanej zmiennej Shopify canonical_url (która wskazuje właściwy adres produktu niezależnie od sposobu wejścia na stronę), dodaje rel="prev"/rel="next" dla paginowanych kolekcji i — jeśli canonical_url nie jest dostępny — używa adresu złożonego z domeny i ścieżki żądania.
Najważniejszy wniosek
Sprawdź, czy twój motyw nie generuje już tagu canonical, zanim dodasz ten kod. Zduplikowane tagi canonical mylą wyszukiwarki bardziej niż ich całkowity brak. Najpierw wyszukaj „canonical” w pliku theme.liquid.
Dane strukturalne organizacji mówią Google, kim jesteś — jaka jest nazwa firmy, logo, profile w mediach społecznościowych i dane kontaktowe. To właśnie ten kod zasila panel wiedzy, który pojawia się, gdy ktoś wyszukuje nazwę twojej marki. Widziałem sklepy, które w ciągu trzech tygodni przeszły od braku panelu wiedzy do w pełni uzupełnionego wyniku marki po poprawnym wdrożeniu tych danych.
Dodaj to wewnątrz <head> w theme.liquid. Kod działa na każdej stronie, a właśnie tego Google oczekuje:
<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>
Zwróć uwagę, że używam filtra | json, a nie | escape. To ważne — json poprawnie escapuje stringi w kontekście JSON-LD, łącznie z dodaniem właściwych cudzysłowów. Używanie escape w JSON-LD to częsty błąd, który kończy się nieprawidłowym kodem. Debugowałem ten konkretny problem w co najmniej kilkunastu sklepach klientów — dane strukturalne przechodziły walidację w narzędziu Google, ale po wdrożeniu po cichu nie działały, bo escaping był błędny.
„JSON-LD to preferowany przez Google format danych strukturalnych. Jeśli wdrażasz schema markup w Shopify, zawsze wybieraj JSON-LD zamiast microdata — jest czystszy, łatwiejszy do debugowania i nie miesza się z twoim HTML odpowiedzialnym za prezentację.”
Dane strukturalne produktu dają ci te wyniki rozszerzone w Google — gwiazdki, cenę, status dostępności. Dawn zawiera podstawowe dane strukturalne produktu, ale często są one niepełne. Poniżej masz bardziej kompletną wersję, którą dopracowałem testami na dziesiątkach sklepów.
Utwórz nowy snippet: 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 -%}
Następnie dołącz go do szablonu produktu (zwykle sections/main-product.liquid albo templates/product.liquid):
{% render 'json-ld-product' %}
Kluczowe detale: używam AggregateOffer zamiast pojedynczych obiektów Offer, bo to czystsze rozwiązanie dla produktów z wieloma wariantami. Pola ratingu korzystają z natywnych metafields recenzji produktów w Shopify — jeśli używasz zewnętrznej aplikacji do opinii, musisz dostosować namespace metafields. (Judge.me i Loox używają innych namespace’ów — sprawdź dokumentację, zanim wkleisz to 1:1.)
Okruszki nawigacyjne pomagają Google zrozumieć hierarchię twojej strony i mogą pojawić się bezpośrednio w wynikach wyszukiwania, zastępując surowy URL. To szczególnie wartościowe w sklepach z głęboką strukturą kategorii. W jednym sklepie, który audytowałem, dodanie danych strukturalnych okruszków nawigacyjnych zmieniło widok w SERP z example.com › collections › summer-2025 › products › red-dress na czytelne Home > Summer Collection > Red Dress — i to mierzalnie poprawiło CTR.
Utwórz snippets/json-ld-breadcrumbs.liquid:
{%- 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 -%}
Dodaj {% render 'json-ld-breadcrumbs' %} w theme.liquid tuż przed </head>. Snippet automatycznie wykrywa typ strony i buduje odpowiedni łańcuch okruszków nawigacyjnych.
Ta zmiana zrobiła największą różnicę w naszych sklepach testowych. Sam preload krojów pisma potrafi urwać pełną sekundę z time-to-text — a w e-commerce ta sekunda ma bezpośredni związek ze współczynnikiem odrzuceń. Własny zespół inżynieryjny Shopify pokazał ten wpływ na shopify.com:
„Dzięki preload frontend developerzy mogą powiedzieć przeglądarce, żeby pobierała pliki fontów równocześnie z plikami CSS. Zaledwie 17 linii kodu skróciło czas wyświetlenia tekstu na shopify.com o 50%.”
Problem jest prosty: przeglądarki nie mogą pobrać krojów pisma, dopóki nie sparsują zarówno HTML, jak i CSS, który się do nich odwołuje. To dwa sekwencyjne round tripy, zanim pojawi się tekst. Preload mówi przeglądarce: „zacznij pobierać ten font teraz, zaraz będzie potrzebny”.
Dodaj to blisko początku <head> w theme.liquid, przed tagami stylesheet:
{%- 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>
Atrybut crossorigin jest wymagany przy preload krojów pisma — bez niego przeglądarka pobierze font dwa razy. Widziałem ten błąd na produkcyjnych sklepach i samo usunięcie podwójnego pobierania poprawiało LCP o 400ms. Wskazówki typu preconnect oszczędzają dodatkowe ~100ms, bo wcześniej zestawiają połączenie TCP + TLS z CDN Shopify.
Najważniejszy wniosek
Preloaduj tylko kroje pisma używane above the fold. Każdy preload konkuruje o przepustowość. Jeśli preloadujesz pięć fontów, to cały sens tej optymalizacji znika — przeglądarka zacznie pobierać wszystkie równolegle i spowolni resztę.
CSS blokujący renderowanie to numer 1 wśród powodów, dla których sklepy Shopify wypadają słabo w Lighthouse. Przeglądarka nie może nic narysować, dopóki nie pobierze i nie sparsuje całego stylesheetu — nawet CSS dla stopki, która jest 3,000px poniżej pierwszego ekranu. To najtrudniejsza optymalizacja na tej liście, ale też ta, która najczęściej daje największy wzrost Lighthouse w moich audytach.
Rozwiązanie: wstaw CSS potrzebny dla treści above the fold bezpośrednio do <head>, a resztę ładuj asynchronicznie.
{%- 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>
Alternatywnie Shopify udostępnia filtr Liquid inline_asset_content, który pozwala wstawić CSS bezpośrednio z plików assets, bez potrzeby tworzenia osobnego snippetu:
<style>{{ 'critical.css' | asset_url | inline_asset_content }}</style>
Poprawne wygenerowanie krytycznego CSS wymaga przeanalizowania każdego szablonu. Polecam użyć narzędzia do generowania krytycznego CSS — wyciąga dokładnie te reguły CSS, które są potrzebne dla treści above the fold na każdym typie strony. Jeśli zrobisz to źle (na przykład pominiesz regułę dla hero section), dostaniesz flash of unstyled content, co jest gorsze niż brak optymalizacji.
Skrypty blokujące parser to drugi największy zabójca wydajności po CSS blokującym renderowanie. Oficjalne wytyczne Shopify są jasne: zminifikowany bundle JS powinien mieć 16KB lub mniej, a wszystko inne powinno być deferowane.
W theme.liquid znajdź swoje tagi <script> i dodaj defer:
{%- 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>
Wzorzec ładowania oparty na interakcji jest agresywny, ale skuteczny. Widgety czatu, popupy z opiniami i podobne dodatki nie muszą ładować się, dopóki użytkownik faktycznie czegoś nie zrobi na stronie. W jednym sklepie, który testowaliśmy, ten wzorzec skrócił Time to Interactive o 340ms — i to była różnica między wynikiem „needs improvement” a „good” dla INP w danych CrUX.
Kiedy ktoś udostępnia twój produkt na Facebooku, Twitterze albo LinkedIn, tagi OG (Open Graph) kontrolują to, co pojawi się w podglądzie. Bez nich platformy społecznościowe zgadują — a zgadują fatalnie. Widziałem udostępnienia produktów, które pokazywały favicon sklepu jako obrazek i tytuł strony checkout jako opis.
Dawn zawiera snippet social-meta-tags.liquid, ale często jest on bardzo podstawowy. Poniżej masz pełniejszą wersję. Utwórz lub zaktualizuj snippets/social-meta-tags.liquid:
{%- 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 -%}
Dołącz go w theme.liquid przez {% render 'social-meta-tags' %} wewnątrz <head>. Wymiary obrazka 1200x630 są zoptymalizowane zarówno pod Facebooka, jak i duży format kart Twitter/X.
Jeśli sprzedajesz w wielu krajach albo językach, tagi hreflang mówią Google, którą wersję strony pokazać której grupie odbiorców. Bez nich twój francuski sklep może pojawiać się w niemieckich wynikach wyszukiwania — i tak, widziałem dokładnie taki przypadek u klienta, który przez miesiące dostawał tickety supportowe w złym języku, zanim odkryto przyczynę.
Shopify Markets automatycznie generuje tagi hreflang dla międzynarodowych domen i subfolderów. Ale jeśli potrzebujesz własnej kontroli — albo używasz aplikacji tłumaczeniowej — dodaj to do <head> w 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 -%}
Tag x-default jest krytyczny — mówi Google, którą wersję pokazać, gdy żaden z określonych języków nie pasuje do preferencji użytkownika. Zawsze kieruj go na swój główny rynek.
Natywny lazy loading jest dziś wspierany przez 92% przeglądarek. Filtr Liquid image_tag w Shopify obsługuje to automatycznie — obrazy po pierwszych trzech sekcjach dostają domyślnie loading="lazy".
Ale musisz dopilnować, żeby nie lazy loadować obrazów above the fold, bo to częsty błąd, który realnie szkodzi LCP. W zeszłym miesiącu audytowałem sklep, który miał loading="lazy" na hero bannerze — dodawało to 800ms do LCP, bo przeglądarka obniżała priorytet najważniejszego obrazu na stronie:
{%- 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'
}}
Atrybut fetchpriority: 'high' na obrazie LCP to nowszy dodatek, który mówi przeglądarce, żeby nadała temu zasobowi wyższy priorytet niż innym. W połączeniu z brakiem lazy loadingu to pojedynczo najbardziej wpływowa rzecz, jaką możesz zrobić dla swojego wyniku LCP.

Zmiany wdrożone. Teraz sprawdź, czy faktycznie działają. (Widziałem programistów, którzy pomijali ten krok i dopiero po trzech miesiącach odkrywali, że ich dane strukturalne były przez cały czas nieprawidłowe.)
Krok 1: Zweryfikuj dane strukturalne. Wejdź do testu wyników z elementami rozszerzonymi od Google i wklej URL produktu. Każdy typ danych strukturalnych, który dodałeś, powinien pojawić się z zielonym checkiem. Typowe błędy: brakujące przecinki w tablicach JSON-LD, nieescapowane cudzysłowy w opisach produktów i nieprawidłowe URL-e obrazów.
Krok 2: Sprawdź tagi canonical. Otwórz źródło strony (Ctrl+U) i wyszukaj „canonical”. Powinieneś zobaczyć dokładnie jeden tag canonical na stronę. Jeśli widzisz dwa, twój motyw już wcześniej dodawał jeden — usuń duplikat.
Krok 3: Uruchom Lighthouse. Otwórz Chrome DevTools → Lighthouse → zaznacz Performance i SEO → Generate Report. Porównaj wyniki przed i po. Zachowaj screeny — przydadzą się, gdy klient zapyta, co właściwie zrobiłeś.
Krok 4: Przetestuj podglądy w mediach społecznościowych. Użyj Facebook's Sharing Debugger i Twitter's Card Validator, żeby sprawdzić, jak wyglądają strony produktów po udostępnieniu.
Typowe błędy, które cię ugryzą:

Oto czego możesz realistycznie oczekiwać po każdej optymalizacji, na podstawie audytów, które robiłem dla sklepów opartych na Dawn:
| Optymalizacja | Oczekiwana zmiana LCP | Oczekiwana zmiana CLS | Punkty Lighthouse |
|---|---|---|---|
| Preload krojów pisma | -300ms do -1.2s | Bez zmian | +3 do +8 |
| Osadzenie krytycznego CSS bezpośrednio w kodzie | -200ms do -800ms | -0.02 do -0.05 | +5 do +15 |
| Odroczone ładowanie niekrytycznego JavaScriptu | -100ms do -500ms | Bez zmian | +3 do +10 |
| Lazy loading obrazów (poprawny) | -100ms do -400ms | Niewielka poprawa | +2 do +5 |
| Dane strukturalne (wszystkie typy) | Bez zmian | Bez zmian | +2 do +5 (wynik SEO) |
| Canonical + hreflang | Bez zmian | Bez zmian | +1 do +3 (wynik SEO) |
| Łącznie | -0.7s do -2.9s | -0.02 do -0.05 | +15 do +35 |
Te liczby mocno zależą od punktu wyjścia. Sklep, który już ma 85+ w Lighthouse, zobaczy mniejsze zyski niż taki, który startuje z 55. Preload krojów pisma i krytyczny CSS najczęściej dają największe poprawy — jeśli masz mało czasu, zacznij właśnie od tych dwóch.
Jeśli edytujesz kod motywu bezpośrednio — nie. Aktualizacje motywu nadpiszą twoje zmiany. Dlatego polecam używać snippets (json-ld-product.liquid, social-meta-tags.liquid itd.) wszędzie, gdzie to możliwe. Snippets przetrwają część aktualizacji. Dla maksymalnego bezpieczeństwa dokumentuj każdą zmianę i wdrażaj ją ponownie po update’ach albo używaj workflow z version control przez Shopify CLI.
Tak, aplikacje takie jak JSON-LD for SEO i Smart SEO obsługują dane strukturalne bez zmian w kodzie. Kompromis jest taki: aplikacje dodają narzut JavaScript (ironiczne przy optymalizacji wydajności), kosztują $5-20/month i dają ci mniejszą kontrolę. Dla preload krojów pisma i krytycznego CSS nie ma sensownego zamiennika dla zmian na poziomie kodu.
Dane strukturalne, tagi OG i kod hreflang działają z każdym motywem Shopify. Składnia preload krojów pisma może się różnić — sprawdź settings_schema.json swojego motywu, żeby znaleźć poprawne nazwy zmiennych fontów. Krytyczny CSS z definicji jest zależny od motywu. Same wzorce kodu Liquid są uniwersalne dla motywów Online Store 2.0.
Użyj testu wyników z elementami rozszerzonymi od Google (search.google.com/test/rich-results) dla pojedynczych URL-i. Dla walidacji w skali całej witryny sprawdź Google Search Console → Enhancements. Google może potrzebować 2-4 tygodni, żeby przetworzyć nowe dane strukturalne i pokazać wyniki w raporcie Enhancements.
defer a async w tagach script?defer pobiera skrypt równolegle, ale czeka z jego wykonaniem, aż HTML zostanie w pełni sparsowany. async też pobiera równolegle, ale wykonuje skrypt od razu, gdy jest gotowy — co może przerwać parsowanie HTML. Dla skryptów motywu zawsze używaj defer. async stosuj tylko dla naprawdę niezależnych skryptów, takich jak narzędzia analityczne, które nie wchodzą w interakcję z DOM.
Te 10 optymalizacji obejmuje zmiany o najwyższym wpływie, jakie możesz wprowadzić w theme.liquid. Ale SEO to coś więcej niż kod. Opisy produktów, linkowanie wewnętrzne, teksty alt obrazów i strategia contentowa mają równie duże znaczenie.
Jeśli chcesz wejść głębiej:
no credit card required
No related articles found.