Bab penutup performa ini soal resource hint: cara ngasih tau browser buat ngambil tindakan sebelum dia nemuin resource-nya secara alami, dan soal navigasi ke halaman berikutnya. Dan pelajaran terbesarnya: Shopify udah ngerjain banyak di sini, jadi bagian tersulit adalah gak ngeduplikasi (atau ngerusak) apa yang platform lakuin.

Resource hint ngasih instruksi ke browser: bikin koneksi lebih awal, fetch aset kritis di muka, atau prerender halaman berikutnya. Shopify nanganin banyak ini otomatis: aset dari CDN dapat HTTP/3, Brotli, dan cache edge global tanpa konfigurasi (57 Performance Dasar, Metrik, dan Cara Ngukur > Apa yang Platform Udah Urus). Yang tersisa di tangan kamu: origin pihak ketiga mana yang di-preconnect, kapan (dan kapan gak) pakai preload, dan konfigurasi Speculation Rules. Satu peringatan buat semua hint: berlebihan sama merugikannya dengan kekurangan, karena hint yang bersaing sama prioritisasi browser sendiri bikin halaman lebih lambat.

Apa yang Platform Lakuin Otomatis

Di tiap respons HTML storefront, Shopify memindai <head> yang dirender buat resource render-blocking: <script src> tanpa async/defer/type="module"/nomodule, dan <link rel="stylesheet"> yang gak dinonaktifkan. Buat resource itu, platform:

  1. Preload sampai 10 di antaranya lewat header Link, yang browser terima sebelum body HTML.
  2. Preconnect ke tiga origin pihak ketiga pertama di antaranya. Origin yang cocok domain toko, host request, atau cdn.shopify.com gak dihitung pihak ketiga.

Dan filter Liquid preload_tag, stylesheet_tag: preload: true, image_tag: preload: true memicu Early Hints (HTTP 103): respons awal berisi header Link yang menghangatkan koneksi dan mulai download sebelum HTML datang, bahkan sementara server masih ngerender Liquid. Ini alasan preload: true di gambar LCP dari 59 Performance Gambar dan LCP > 2. Early Hints begitu efektif.

Konsekuensinya: hint yang kamu tulis di HTML buat origin atau resource yang udah dicakup otomatis itu redundan, dan ditemukan lebih telat dari header. Sebelum nambahin hint apapun, cek header Link di request dokumen di Network panel. Kalau origin/resource-nya udah ada di situ, hint kamu gak nambah apa-apa.

preconnect

Waktu browser parse preconnect, dia ngelakuin seluruh handshake koneksi: DNS lookup, TCP, dan TLS. Resource dari domain itu bisa langsung pakai koneksi yang siap. Dampak per koneksi kecil, tapi kumulatif dengan banyak domain eksternal. preconnect lebih dari dns-prefetch yang cuma DNS.

Kebanyakan theme gak butuh hint preconnect. Tambahin cuma buat origin yang ditemukan telat, yang platform gak bisa lihat di <head> yang dirender: origin yang pertama diminta oleh JavaScript saat runtime, oleh app embed, atau oleh resource jauh di <body>. Contoh: host video yang baru dihubungi setelah player inisialisasi, atau provider widget pembayaran yang script-nya disuntik script lain.

Jangan preconnect ke: origin yang udah nyajiin script/stylesheet render-blocking di <head> (Shopify udah), domain toko sendiri (udah terkoneksi), domain yang halaman gak pakai, domain yang udah kamu preload (redundan), dan domain Shopify (cdn.shopify.com, monorail-edge.shopifysvc.com) yang koneksinya platform kelola. Khusus cdn.shopify.com: platform udah kirim di header Link, dan URL aset sekarang pakai path /cdn di domain toko sendiri, jadi halaman mungkin gak ngeload apa-apa dari cdn.shopify.com, koneksi ekstra itu biaya murni. Theme Check CdnPreconnect nandain ini.

1. Atribut crossorigin

<link rel="preconnect" href="https://external-domain.com" crossorigin />

crossorigin ngasih tau browser buat buka koneksi dalam mode CORS. Tambahin cuma kalau resource pertama yang di-fetch dari domain itu pakai CORS (font, fetch()). Hilangkan buat domain yang nyajiin resource non-CORS (script biasa, stylesheet, gambar). Browser misahin koneksi CORS dan non-CORS, jadi ketidakcocokan berarti socket yang di-preconnect gak bisa dipakai ulang dan browser buka koneksi kedua, ngebatalin tujuannya. Domain yang nyajiin keduanya (font CORS dan CSS non-CORS) butuh dua hint:

<link rel="preconnect" href="https://example.com">
<link rel="preconnect" href="https://example.com" crossorigin>

2. Batasi Satu atau Dua

Preconnect punya biaya, di atas tiga origin yang Shopify udah preconnect. Identifikasi satu atau dua domain pihak ketiga yang ditemukan telat dan paling kritis. Pakai dns-prefetch buat yang kurang kritis, dan jangan dua-duanya buat domain yang sama.

3. Contoh yang Masih Layak

Provider font: origin stylesheet-nya ada di <head> jadi Shopify udah preconnect, tapi file font-nya dari origin kedua yang baru ditemukan setelah CSS di-parse:

{%- comment -%}
  The font files are requested from inside the stylesheet, so this origin isn't
  visible in the rendered `<head>` and the platform can't preconnect to it.
{%- endcomment -%}
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

Tapi self-hosting font di CDN Shopify (60 Performance CSS dan Font > Self-host Font di CDN Shopify) ngilangin kebutuhan hint ini sepenuhnya. YouTube kalau halaman punya video embed, tanpa crossorigin karena iframe dan thumbnail itu request non-CORS:

{%- if section.settings.video_url contains 'youtube' -%}
  <link rel="preconnect" href="https://www.youtube.com">
  <link rel="preconnect" href="https://i.ytimg.com">
{%- endif -%}

❌ redundan dan bisa jadi biaya murni

<link rel="preconnect" href="https://cdn.shopify.com">

✅ origin yang beneran ditemukan telat

<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

Cek di Network panel: sebelum, bar koneksi (oranye dan pink) muncul waktu resource ditemukan; sesudah, koneksi udah siap dan resource langsung download.

preload yang Hemat

preload nyuruh browser download resource segera dengan prioritas High. Preload aset yang udah ada di HTML gak ngasih manfaat karena preload scanner nemuinnya cepat. Preload banyak resource ngerusak prioritisasi keseluruhan dan bisa nunda resource kritis. Bedanya sama fetchpriority dari 59 Performance Gambar dan LCP: preload menemukan resource yang browser bakal temukan telat, fetchpriority="high" naikin prioritas resource yang browser udah tau.

1. Anggaran 10 Slot

Shopify kirim maksimal 10 header Link preload per respons, dan preload render-blocking otomatis ditempatin duluan. Hint kamu ngisi slot sisanya dengan urutan:

  1. Preload render-blocking otomatis (dari <head>).
  2. Preload stylesheet (stylesheet_tag: preload: true).
  3. Preload lain, termasuk font dari preload_tag.
  4. Preload script.
  5. Preload gambar (image_tag: preload: true).

Apapun setelah entri kesepuluh dibuang. <head> dengan 10+ resource render-blocking gak nyisain ruang sama sekali, jadi preload gambar LCP, yang diurutkan terakhir, jadi yang pertama hilang. Kalau preload yang kamu tambahin gak muncul di header Link dokumen, cek berapa resource render-blocking di <head> sebelum nyelidiki hal lain. Ini alasan lain buat ngurangin stylesheet dari bab 60: tiap satu ngeblokir render dan bersaing buat slot preload.

2. Kapan

Valid: font yang dirujuk telat di file CSS terpisah, file kritis yang ditemukan telat waktu parsing, dan setelah tes membuktikan bermanfaat. Jangan: script/stylesheet render-blocking di <head> (udah otomatis), aset yang udah di HTML, lebih dari dua resource, tanpa tes, atau buat “memperbaiki” rendering JavaScript (perbaiki arsitekturnya).

3. Pakai Filter Liquid

Filter Liquid lebih baik dari HTML polos karena memicu Early Hints:

{%- comment -%} Liquid filters trigger Early Hints {%- endcomment -%}
{{ 'font.woff2' | asset_url | preload_tag: as: 'font', type: 'font/woff2' }}
{{ 'critical.css' | asset_url | stylesheet_tag: preload: true }}
{{ product.featured_image | image_url: width: 800 | image_tag: preload: true }}

Shopify ngubah filter jadi header Link otomatis → HTTP 103 → browser nerima instruksi preload sebelum HTML utama → aset mulai download lebih awal dari tag HTML. Catatan: filter Liquid cuma buat aset CDN Shopify, aset eksternal butuh <link rel="preload"> HTML di <head>.

Contoh font yang ditemukan telat: tanpa preload, browser gak tau font sampai CSS download dan parse; dengan preload, font download paralel sama CSS:

{{ 'font.woff2' | asset_url | preload_tag: as: 'font', type: 'font/woff2' }}
<link rel="stylesheet" href="{{ 'styles.css' | asset_url }}">

❌ gak berguna, preload scanner udah nemuin

<link rel="preload" href="hero.jpg" as="image" />
<img src="hero.jpg" alt="Hero" />

✅ beneran ditemukan telat

{{ 'font.woff2' | asset_url | preload_tag: as: 'font', type: 'font/woff2' }}

Cek header Link di request dokumen (preload yang Shopify kirim, termasuk otomatis, dan apakah hint kamu lolos batas 10), Priorities Bookmarklet buat audit total preload (≤ 2 milik kamu), dan tes sebelum/sesudah beberapa run.

Speculation Rules

Speculation Rules API nyatain link mana yang harus di-prefetch atau di-prerender buat navigasi lebih cepat. Ini trade-off performa vs konsumsi resource: prerender halaman yang gak pernah dikunjungi ngebuang data pengguna dan resource browser, bisa nembak event analitik prematur, dan ngasilin konten basi kalau state (cart) berubah setelah prerender.

1. Default Shopify

Sejak Juni 2025, Shopify ngaktifin konfigurasi platform-wide buat semua storefront Liquid: header Speculation-Rules yang nunjuk ke file JSON. Default-nya prefetch dengan eagerness conservative: browser prefetch waktu pengguna touchdown atau mousedown. Hasilnya (dari blog Speculation Rules at Shopify): navigasi same-site di Chromium ~220ms lebih cepat di median desktop dan ~20ms di mobile, cuma nambah ~0,7% trafik SFR, dan ~10% pengguna dapat HTML sebelum navigasi mulai. Didukung Chromium (Chrome, Edge, Opera); Safari dan Firefox ngabaikan diam-diam (gak rusak, gak dapat manfaat). Shopify otomatis kirim header Clear-Site-Data buat ngosongin cache prefetch/prerender waktu cart berubah, tapi perubahan state lain harus kamu tangani.

2. Aturan Sendiri

Tambahin aturan yang lebih agresif lewat <script type="speculationrules"> di <head>, diproses selain default Shopify. Buat navigasi berkeyakinan tinggi: langkah berikutnya di checkout, atau “View product” dari koleksi. Contoh prerender link dengan selector tertentu waktu hover (moderate):

<script type="speculationrules">
  {
    "prerender": [
      {
        "where": {
          "selector_matches": "[data-instant-navigation]"
        },
        "eagerness": "moderate"
      }
    ]
  }
</script>
<a href="{{ product.url }}" data-instant-navigation>
  View product
</a>

prerender pakai hati-hati: analitik prematur dan konten basi.

3. prefetch vs prerender dan Eagerness

  1. prefetch: ngambil respons tapi gak render atau eksekusi kode. Lebih aman, biaya rendah.
  2. prerender: ngambil dan render termasuk eksekusi JavaScript. Biaya lebih tinggi, navigasi lebih cepat.

Aturan bisa nargetin URL lewat daftar, pola, atau selector CSS. eagerness ngatur seberapa kuat sinyal yang browser tunggu, dari paling agresif:

  1. immediate: begitu aturan di-parse, tanpa interaksi. Prerender semua link yang cocok, mahal di halaman koleksi.
  2. eager: sinyal ringan: kursor bergerak ke arah link, hover sesaat, scroll berhenti dengan link menonjol. (Bukan “segera”, itu immediate, yang ditambahin waktu eager didefinisikan ulang di antara immediate dan moderate.)
  3. moderate: perilaku yang nunjukin navigasi kemungkinan besar: kursor diam di atas link, di Chrome hover ~200ms.
  4. conservative: touchdown/mousedown, momen paling dekat klik, biaya terendah.

Tanpa eagerness, document rules default conservative, list rules default immediate. Dari conservative ke moderate naikin rate prefetch ~0,7–1,0 poin persentase dari baseline 12–14%, nambah ~2–4% request SFR. moderate trade-off terbaik buat kebanyakan theme: cukup awal buat berguna, gak ngebuang resource di link yang pengguna gak dekati.

Cek di Application > Speculative loads (Background services) buat lihat halaman mana yang dispekulasi dan berhasil atau gak, dan Performance panel buat bandingin navigasi biasa vs terspekulasi. Buat Hydrogen, padanannya prefetch="intent"/"viewport" bawaan.

Semua Aset dari CDN Shopify

Shopify nyajiin aset theme lewat CDN-nya dengan URL di domain toko sendiri di path /cdn, jadi same-origin dan pakai ulang koneksi yang ada. Tiap domain tambahan yang halaman rujuk butuh koneksi baru sebelum byte pertama: DNS + TCP + TLS = sampai tiga round trip. Di mobile dengan latensi 150ms, itu hampir setengah detik sebelum aset mulai download, per domain. Kalau file eksternalnya stylesheet render-blocking atau hero, langsung ke FCP dan LCP.

Caranya: pindahin file yang di-host eksternal dan bagian dari theme ke assets/, rujuk lewat asset_url (Shopify milih host dan nambah versi), tanpa hardcode domain CDN:

{%- comment -%} Before: three external domains, three connection setups {%- endcomment -%}
<link rel="stylesheet" href="https://cdn.example-ui.com/example-ui@3/example.css">
<script src="https://scripts.example-cdn.com/slider-3.7.1.min.js"></script>
<img src="https://images.example-cdn.com/logo.svg" alt="Store logo" width="240" height="80">
{%- comment -%} After: same-origin, versioned, and no extra connections {%- endcomment -%}
{{ 'theme.css' | asset_url | stylesheet_tag }}
<script src="{{ 'theme.js' | asset_url }}" type="module"></script>
<img
  src="{{ 'logo.svg' | asset_url }}"
  alt="Store logo"
  width="240"
  height="80"
/>

Kalau aset pihak ketiga gak bisa dipindahin, tambahin preconnect dari bagian di atas.

1. Filter yang Tepat per Sumber Gambar

Dua-duanya dari CDN Shopify, tapi pilihannya nentuin apakah gambar bisa di-resize saat request:

  1. Gambar yang ikut theme di assets/ → asset_url. Shopify nyajiin apa adanya, jadi kamu nulis <img> sendiri dengan alt, width, height eksplisit (tanpa dimensi, layout bergeser). Buat aset ukuran tetap yang gak butuh resize: logo, ikon.
  2. Gambar dari merchant (setting image_picker atau upload lewat Files) → image_url + image_tag, yang resize di CDN dan ngasilin srcset, alt, width, height, dengan sizes dari kamu.

Gambar besar di assets/ gak bisa di-resize, jadi kalau gambar statis butuh transformasi responsif, pindahin ke Files. Dan asset_url | image_tag gak didukung: asset_url ngembaliin string URL, image_tag butuh metadata dari image_url. Soal quality di image_url (10–90): hilangkan kecuali kamu udah ngukur file lebih kecil di nilai tertentu, karena Shopify milih otomatis berdasarkan format output dan hasilnya sering lebih kecil dari nilai tinggi eksplisit.

2. Versioning dan CSS

asset_url nambah nomor versi (?v=1384022871) yang berubah waktu file diupdate, maksa browser download versi baru. Tanpa itu, cache basi bisa berhari-hari. Buat gambar di CSS, pakai asset_url di file .css.liquid (dirujuk sebagai style.css tanpa .liquid):

.hero {
  background: url({{ 'bg.jpg' | asset_url }});
}

Cek di Network panel: tambahin kolom Domain dan urutkan buat daftar semua host yang halaman hubungi, audit semua selain domain toko (kecuali app dan layanan yang gak bisa disalin). Tab Timing aset same-origin nunjukin 0ms buat DNS Lookup, Initial connection, dan SSL; nilai bukan nol itu overhead yang hilang waktu dipindah ke assets/.

Defer Resource Non-kritis

Rangkuman keputusan defer yang tersebar di bab 60 dan 61, dalam satu tabel:

  1. CSS section below the fold: ya, pola async CSS dengan section.index (tes positif > N, nil tetap sinkron).
  2. CSS section above the fold: tidak, tetap sinkron. Chrome DevTools nandain semua resource render-blocking, tapi itu bukan berarti semuanya harus dihilangkan: critical CSS harus ngeblokir render buat nyegah FOUC dan CLS. Evaluasi tiap resource.
  3. JavaScript interaksi theme: ya, tag <script defer> manual, karena filter script_tag gak ndukung defer/async:
<script src="{{ 'theme-interactions.js' | asset_url }}" defer></script>
  1. Analitik dan tracking: ya, setelah DOMContentLoaded atau saat interaksi.
  2. Script app lewat ScriptTag API: tidak (bukan kontrol theme), minta developer app pakai defer.
  3. content_for_header: tidak, dikelola platform.
  4. Anti-flicker A/B testing: tidak by design, hapus kalau gak ada tes.

Selalu cek Render blocking requests di Summary Performance panel, waterfall Network, FCP sebelum/sesudah, dan inspeksi visual buat FOUC dan layout shift baru.

Ringkasan

  1. Platform otomatis di tiap respons: preload sampai 10 resource render-blocking dari <head> lewat header Link, preconnect ke 3 origin pihak ketiga pertama, dan Early Hints (HTTP 103) dari filter preload_tag/stylesheet_tag: preload: true/image_tag: preload: true. Cek header Link dulu sebelum nambah hint.
  2. preconnect cuma buat origin yang ditemukan telat (runtime JavaScript, app embed, jauh di body, font dari dalam CSS), 1–2 domain, crossorigin cuma buat resource CORS (dua hint kalau campur), jangan ke Shopify/cdn.shopify.com (CdnPreconnect)/domain sendiri/yang udah di-preload.
  3. preload ≤ 2 resource yang beneran ditemukan telat, lewat filter Liquid (Early Hints, cuma CDN Shopify), jangan buat yang udah di HTML/<head>; anggaran 10 slot diisi otomatis dulu, gambar LCP terakhir, jadi kurangi resource render-blocking.
  4. Speculation Rules: default Shopify prefetch + conservative sejak Juni 2025 (~220ms desktop), Chromium aja, Clear-Site-Data saat cart berubah. Aturan sendiri lewat <script type="speculationrules"> dengan selector_matches, prerender hati-hati (analitik, konten basi), eagerness immediate/eager/moderate (~200ms hover, terbaik)/conservative.
  5. Semua aset dari CDN Shopify (/cdn same-origin, tanpa DNS/TCP/TLS ekstra): pindahin ke assets/ + asset_url (aset tetap, <img> manual dengan dimensi) atau Files + image_url | image_tag (resize, srcset), asset_url | image_tag gak jalan, quality dihilangkan, .css.liquid buat background, versioning ?v=.
  6. Defer: CSS below fold (async pattern), JS theme (<script defer> manual), analitik (setelah load/interaksi); jangan CSS above fold, content_for_header, ScriptTag app (minta developer), anti-flicker.

Bab selanjutnya kita masuk bagian tooling lanjutan dengan Theme Check: linter resmi Shopify yang nangkep error Liquid, JSON, terjemahan, performa, dan aksesibilitas sebelum deploy. Cara install, konfigurasi .theme-check.yml, preset, dan integrasi ke editor dan CI.