Di 10 Tags Variabel dan Percabangan dan 11 Tags Perulangan kita belajar tag Liquid umum yang ada di semua implementasi Liquid. Sekarang tag-tag yang cuma ada di Shopify, yang bikin file-file theme bisa saling manggil dan ngobrol sama toko.

Tag di bab ini itu perekat arsitektur theme. Tanpa mereka, template gak bisa manggil section, section gak bisa manggil snippet, dan tombol “tambah ke keranjang” gak bisa jalan. Jadi walaupun bab ini masih di Bagian 2 (Liquid), isinya bakal terus dirujuk di Bagian 3 (Arsitektur).

render

Ngerender snippet atau app block. Ini tag yang paling sering dipakai dari semua tag di bab ini:

{% render 'filename' %}

filename itu nama file snippet tanpa ekstensi .liquid. Jadi {% render 'product-card' %} bakal nyari snippets/product-card.liquid.

1. Aturan Scope: Snippet Itu Kotak Tertutup

Ini konsep paling penting soal render: di dalam snippet, kamu gak bisa ngakses variabel yang dibikin di luar snippet. Snippet itu kaya ruangan tertutup, variabel dari luar gak masuk kecuali kamu kirim lewat pintu (parameter).

Yang bisa diakses dari dalam snippet:

  1. Object global (shop, settings, request, dan seterusnya).
  2. Object yang emang tersedia di tempat snippet-nya dipanggil. Snippet di template produk bisa akses product, snippet di dalam section bisa akses section.

Yang gak bisa:

  1. Variabel hasil assign atau capture di luar snippet.
  2. Dan sebaliknya: variabel yang dibikin di dalam snippet gak bisa diakses dari luar.

❌ salah, title_text gak kelihatan dari dalam snippet

{% assign title_text = 'Produk Unggulan' %}
{% render 'heading' %}
 
{% comment %} di snippets/heading.liquid {% endcomment %}
<h2>{{ title_text }}</h2>

✅ benar, kirim lewat parameter

{% assign title_text = 'Produk Unggulan' %}
{% render 'heading', title: title_text %}
 
{% comment %} di snippets/heading.liquid {% endcomment %}
<h2>{{ title }}</h2>

Kenapa dibikin tertutup gini? Karena ini bikin snippet bisa diprediksi: kamu tau persis data apa yang masuk, jadi snippet yang sama bisa dipakai di 20 tempat berbeda tanpa takut ketimpa variabel dari luar. Ini juga alasan kenapa LiquidDoc di 07 Tools Lokal VS Code Extension, Prettier, LiquidDoc > LiquidDoc bisa ngecek parameter: pintunya cuma satu.

Satu aturan tambahan: di dalam snippet yang dirender pakai render, kamu gak bisa pakai tag include.

2. Ngirim Variabel

Sintaksnya nama parameter diikuti titik dua dan nilainya, boleh lebih dari satu dipisah koma:

{% render 'filename', variable: value %}

Contoh:

{% render 'product-card', product: product, show_vendor: true, image_size: 'large' %}

Dan ini penting: perubahan yang kamu bikin ke variabel yang dikirim cuma berlaku di dalam snippet. Kalau snippet-nya ngubah show_vendor jadi false, di luar tetap true.

3. Parameter with

Ngirim satu object ke snippet, dengan as opsional buat ngasih nama:

{% render 'filename' with object as name %}

Kalau as gak dipakai, object-nya bisa diakses pakai nama file snippet-nya:

{% comment %} di dalam snippet, object-nya bernama 'product-card' {% endcomment %}
{% render 'product-card' with product %}
 
{% comment %} di dalam snippet, object-nya bernama 'item' {% endcomment %}
{% render 'product-card' with product as item %}

Praktisnya, cara variable: value lebih jelas dan lebih umum dipakai. with itu warisan dari Liquid lama.

4. Parameter for

Ngerender snippet buat tiap item di array, kaya for loop tapi langsung ke snippet:

{% render 'filename' for array as item %}

Contoh:

{% render 'product-card' for collection.products as product %}

Ini sama dengan:

{% for product in collection.products %}
  {% render 'product-card', product: product %}
{% endfor %}

Bonusnya, object forloop tersedia di dalam snippet, jadi snippet-nya bisa tau dia item pertama atau terakhir.

include (Deprecated)

Ini tag lama buat ngerender snippet. Bedanya sama render: di dalam snippet yang di-include, kamu bisa ngakses dan ngubah variabel dari luar. Kedengeran praktis, tapi justru itu masalahnya.

include udah deprecated karena cara nanganin variabelnya nurunin performa dan bikin kode susah dibaca dan dirawat. Bayangin snippet yang bisa diam-diam ngubah variabel di file yang manggilnya, terus dipanggil dari 20 tempat. Nyari bug-nya kaya nyari jarum di jerami.

❌ deprecated, jangan dipakai di kode baru

{% include 'product-card' %}

✅ pakai render

{% render 'product-card', product: product %}

Kamu masih bakal nemuin include di theme lama. Kalau lagi migrasi, ganti ke render dan kirim variabelnya secara eksplisit. Theme Check punya aturan buat nandain ini, dibahas di 66 Theme Check Referensi Aturan A–L.

section

Ngerender section secara statis, artinya section-nya nempel di posisi itu dan merchant gak bisa mindahin atau ngehapusnya lewat theme editor:

{% section 'name' %}

Contoh paling umum, di layout:

{% section 'header' %}

Ini bakal nyari sections/header.liquid dan ngerendernya, dibungkus <div id="shopify-section-header" class="shopify-section section-header"> yang ditambahin otomatis sama Shopify. Bahasan lengkap section di 26 Sections dan Section Schema.

Di theme modern, section statis ini udah jarang dipakai buat header dan footer, digantiin sections (dengan s) di bawah yang bikin merchant bisa ngatur isinya. Tapi masih berguna buat bagian yang emang gak boleh dipindah.

sections

Ngerender section group, yaitu wadah section yang bisa diatur merchant. Taruh di layout di posisi yang kamu mau:

{% sections 'name' %}

Contoh:

{% sections 'header-group' %}

Ini bakal nyari sections/header-group.json (perhatiin, JSON, bukan Liquid) yang berisi daftar section apa aja yang ada di grup itu dan urutannya. Merchant bisa nambah, ngehapus, dan ngurutin section di dalam grup lewat theme editor. Bahasannya di 27 Section Groups dan Migrasinya.

Bedanya section dan sections gampang diinget: section (tunggal) = satu section yang dipaku, sections (jamak) = sekelompok section yang bisa diatur merchant.

content_for

Bikin area khusus buat ngerender block. Butuh parameter tipe buat ngebedain dua kegunaan:

{% content_for 'blocks' %}
{% content_for 'block', type: "slide", id: "slide-1" %}

1. content_for ‘blocks’

Bikin area yang ngerender theme blocks sesuai konfigurasi di JSON template atau section group. Merchant bisa nambah, ngehapus, dan ngurutin block-nya lewat theme editor:

{% content_for "blocks" %}

Ini yang dipakai di dalam section buat bilang “block-block yang diatur merchant, munculnya di sini”. Bahasannya di 29 Theme Blocks Mendalam.

2. content_for ‘block’

Ngerender satu static block dengan tipe dan ID tertentu. Static block itu block yang posisinya dipaku oleh developer, merchant cuma bisa ngatur setting-nya, gak bisa mindahin:

{% content_for "block", type: "button", id: "static-block-1", color: "red" %}

Kamu bisa ngirim parameter tambahan apapun (kaya color di atas) yang bisa diakses di dalam block pakai {{ color }}. Mirip kaya ngirim variabel ke snippet. Bahasannya di 29 Theme Blocks Mendalam.

layout

Nentuin layout mana yang dipakai template ini:

{% layout name %}

Bawaannya semua template pakai layout/theme.liquid. Tag ini bikin kamu bisa pakai layout lain, atau gak pakai layout sama sekali:

{% layout 'full-width' %}
{% layout none %}

{% layout 'full-width' %} nyari layout/full-width.liquid. {% layout none %} ngerender template tanpa header, footer, atau <head> apapun, cuma isi template-nya doang. Ini kepakai buat halaman khusus kaya gift card yang mau dicetak, atau halaman yang di-embed di tempat lain. Bahasannya di 22 Layouts.

form

Ini tag yang ngehidupin toko. form ngasilin tag HTML <form> lengkap dengan semua <input> tersembunyi yang dibutuhin buat ngirim data ke endpoint Shopify yang tepat. Kamu gak perlu tau URL-nya, gak perlu tau field rahasianya, Shopify yang ngurus:

{% form 'form_type' %}
  content
{% endform %}

Karena jenis form di toko itu banyak, form wajib dikasih tipe. Beberapa tipe butuh parameter tambahan.

1. product

Form buat nambahin varian produk ke keranjang. Wajib dikasih object product:

{% form 'product', product %}
  <!-- form content -->
{% endform %}

Hasilnya:

<form method="post" action="/cart/add" id="product_form_6786188247105" accept-charset="UTF-8" class="shopify-product-form" enctype="multipart/form-data"><input type="hidden" name="form_type" value="product" /><input type="hidden" name="utf8" value="✓" />
  <!-- form content -->
<input type="hidden" name="product-id" value="6786188247105" /></form>

Perhatiin: action="/cart/add", method="post", dan tiga <input type="hidden"> semuanya dibikinin otomatis. Kamu cuma perlu nambahin <input name="id"> buat ID varian dan tombol submit. Ini form yang bakal kamu bedah di 24 Template per Halaman.

❌ salah, nulis form manual, gampang salah dan gak dapet field tersembunyinya

<form action="/cart/add" method="post">
  <input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
  <button>Add to cart</button>
</form>

✅ benar, pakai tag form

{% form 'product', product %}
  <input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
  <button>Add to cart</button>
{% endform %}

2. cart

Form buat bikin checkout dari isi keranjang. Wajib dikasih object cart:

{% form 'cart', cart %}
  <!-- form content -->
{% endform %}

Hasilnya action="/cart" dengan id="cart_form". Ini form di halaman keranjang yang tombol “Checkout”-nya ada di dalamnya.

3. contact

Form buat ngirim email ke merchant, alias form kontak:

{% form 'contact' %}
  <!-- form content -->
{% endform %}

Bahasannya di 46 Customer Sign-in, Account Component, Contact Form, Email Consent.

4. customer

Form buat bikin pelanggan baru tanpa registrasi akun. Gunanya buat ngumpulin data pelanggan tanpa nyuruh mereka login, kaya daftar newsletter:

{% form 'customer' %}
  <!-- form content -->
{% endform %}

Jangan ketuker sama create_customer di bawah yang beneran bikin akun.

5. Form Akun Pelanggan

Sekumpulan form buat urusan akun:

  1. create_customer, bikin akun pelanggan baru (registrasi). Dipakai di template customers/register.
  2. customer_login, login. Dipakai di template customers/login.
  3. guest_login, balik ke sesi checkout sebagai tamu tanpa login. Dipakai di template customers/login juga.
  4. recover_customer_password, “lupa password”. Dipakai di customers/login.
  5. reset_customer_password, reset password. Dipakai di customers/reset_password.
  6. activate_customer_password, aktivasi akun. Dipakai di customers/activate_account.
  7. customer_address, bikin atau edit alamat. Wajib dikasih parameter: customer.new_address kalau bikin alamat baru, atau object address kalau ngedit yang ada.

Contoh customer_address:

{% form 'customer_address', customer.new_address %}
  <!-- form content -->
{% endform %}

Semua template customers/* dibahas di 24 Template per Halaman.

6. localization

Form buat pelanggan milih negara dan bahasa, biar dapat mata uang dan bahasa yang sesuai. Isinya bisa pemilih negara, pemilih bahasa, atau dua-duanya:

{% form 'localization' %}
  <!-- form content -->
{% endform %}

Form ini menggantikan form currency yang udah deprecated. Kalau nemu {% form 'currency' %} di theme lama, itu yang harus diganti. Bahasannya di 47 Markets Multi Currency, Multi Bahasa, Localization.

7. new_comment

Form buat komentar di artikel blog. Wajib dikasih object article:

{% form 'new_comment', article %}
  <!-- form content -->
{% endform %}

8. storefront_password

Form buat masukin password toko yang dikunci. Dipakai di template password:

{% form 'storefront_password' %}
  <!-- form content -->
{% endform %}

9. Parameter return_to

Bawaannya tiap tipe form ngarahin pelanggan ke halaman tertentu setelah submit. Form product misalnya ngarahin ke halaman keranjang. return_to bikin kamu bisa ngubah tujuannya:

  1. back, balik ke halaman sebelum submit.
  2. Path relatif, misalnya /collections/all.
  3. Atribut routes, misalnya routes.root_url.
{% form 'customer_login', return_to: routes.root_url %}
  <!-- form content -->
{% endform %}

Hasilnya nambahin <input type="hidden" name="return_to" value="/" />.

10. Atribut HTML

Kamu bisa nambahin atribut HTML apapun ke tag <form>-nya dengan nulis nama atributnya sebagai parameter:

{% form "product", product, id: 'custom-id', class: 'custom-class', data-example: '100' %}
  <!-- form content -->
{% endform %}

Ini berguna buat ngasih class CSS atau atribut data-* yang dibaca JavaScript.

javascript dan stylesheet

Dua tag ini buat naruh JavaScript dan CSS langsung di dalam file section, block, atau snippet, biar kode yang berhubungan ngumpul di satu file:

{% javascript %}
  javascript_code
{% endjavascript %}
{% stylesheet %}
  css_styles
{% endstylesheet %}

Aturannya:

  1. Tiap file cuma boleh punya satu {% javascript %} dan satu {% stylesheet %}.
  2. Liquid gak dirender di dalamnya. Kalau kamu nulis {{ settings.color }} di dalam {% stylesheet %}, itu bakal dianggap teks biasa dan bikin error sintaks CSS.
  3. Shopify ngumpulin semua isi tag ini dari seluruh theme jadi satu file yang di-load sekali, jadi gak duplikat walaupun section-nya dipakai 10 kali di satu halaman.

Cara load dan aturan lengkapnya dibahas di 54 Best Practices Umum.

❌ salah, Liquid gak jalan di dalam stylesheet

{% stylesheet %}
  .card { color: {{ settings.text_color }}; }
{% endstylesheet %}

✅ benar, buat CSS yang butuh nilai dari Liquid, pakai style

{% style %}
  .card { color: {{ settings.text_color }}; }
{% endstyle %}

style

Beda sama stylesheet, tag style ngasilin tag HTML <style data-shopify> biasa, dan Liquid di dalamnya dirender:

{% style %}
  .h1 {
    color: {{ settings.colors_accent_1 }};
  }
{% endstyle %}

Hasilnya:

<style data-shopify>
  .h1 {
    color: #121212;
  }
</style>

Ini yang dipakai buat CSS yang nilainya dari setting. Dan ingat dari 06 Theme Editor dan Code Editor > Live Preview: setting warna yang dirujuk di dalam {% style %} bakal update langsung di theme editor tanpa refresh, dengan batasan soal filter yang udah dibahas di sana.

Ringkasnya tiga tag CSS ini:

  1. stylesheet: CSS statis per file, dikumpulin jadi satu, tanpa Liquid.
  2. style: CSS dinamis dengan Liquid, dirender jadi <style> di tempat.
  3. File .css di assets/: CSS besar yang di-load lewat <link>.

doc

Tag buat dokumentasi yang udah kita bahas panjang lebar di 07 Tools Lokal VS Code Extension, Prettier, LiquidDoc > LiquidDoc. Isinya gak dirender, Liquid di dalamnya di-parse tapi gak dieksekusi:

{% doc %}
  Renders a message.
  @param {string} foo - A string value.
  @param {string} [bar] - An optional string value.
  @example
  {% render 'message', foo: 'Hello', bar: 'World' %}
{% enddoc %}

break di Luar Loop

Di 11 Tags Perulangan > break dan continue kita lihat break buat berhentiin loop. Tapi di luar loop, break berhentiin sisa file dari dirender, kaya early return di fungsi. Output yang udah dirender sebelumnya tetap ada.

Karena section, theme block, dan snippet yang dirender pakai render masing-masing punya konteks sendiri, cuma file yang berisi break itu yang berhenti. File yang manggilnya dan sisa halaman gak terpengaruh.

Contoh theme block breadcrumb yang gak perlu muncul di halaman depan:

{%- if template.name == 'index' -%}
  {%- break -%}
{%- endif -%}
<nav aria-label="Breadcrumbs">
  <a href="{{ routes.root_url }}">Home</a>
  <span aria-hidden="true">/</span>
  <span>{{ page_title }}</span>
</nav>

Di halaman depan, block ini gak ngeluarin apa-apa karena berhenti di baris kedua. Ini jauh lebih rapi daripada ngebungkus seluruh isi file pakai {% unless %}.

❌ ribet, seluruh file harus dibungkus dan di-indent

{%- unless template.name == 'index' -%}
  <nav aria-label="Breadcrumbs">
    ...
  </nav>
{%- endunless -%}

✅ rapi, cek di atas, keluar lebih awal

{%- if template.name == 'index' -%}
  {%- break -%}
{%- endif -%}
<nav aria-label="Breadcrumbs">
  ...
</nav>

Satu peringatan: snippet yang dirender pakai include (yang deprecated itu) berbagi konteks sama file yang manggilnya, jadi break di snippet itu ikut berhentiin file pemanggilnya. Satu alasan lagi buat gak pakai include.

Ringkasan

  1. render ngerender snippet dari snippets/, dan snippet itu kotak tertutup: variabel luar gak masuk kecuali dikirim lewat parameter, variabel dalam gak keluar.
  2. Kirim variabel pakai nama: nilai, atau with object as name, atau for array as item yang sekalian ngasih forloop.
  3. include deprecated karena snippet-nya bisa ngubah variabel luar, yang bikin performa turun dan kode susah dirawat.
  4. section (tunggal) ngerender satu section yang dipaku, sections (jamak) ngerender section group dari file JSON yang bisa diatur merchant.
  5. content_for 'blocks' bikin area theme blocks yang diatur merchant, content_for 'block' ngerender satu static block dengan tipe dan ID tetap.
  6. layout ganti layout template, atau layout none buat tanpa layout sama sekali.
  7. form ngasilin <form> lengkap dengan input tersembunyi yang dibutuhin Shopify. Tipe utamanya: product (tambah ke keranjang), cart (checkout), contact, customer (newsletter), form-form akun, localization, new_comment, dan storefront_password.
  8. return_to ngatur tujuan setelah submit, dan atribut HTML bisa ditambahin sebagai parameter.
  9. javascript dan stylesheet cuma boleh satu per file, dikumpulin jadi satu file oleh Shopify, dan Liquid gak dirender di dalamnya.
  10. style ngasilin <style> biasa dengan Liquid yang dirender, dipakai buat CSS yang nilainya dari setting, dan mendukung live preview.
  11. break di luar loop berhentiin sisa file itu aja, kaya early return, tanpa ngaruh ke file pemanggil.

Bab selanjutnya kita mulai bedah object-object Liquid, dimulai dari yang global dan bisa dipakai di mana aja: shop, request, routes, settings, localization, cart, customer, template, dan lainnya.