Plang reference

Objects

Every global a Plang template can read: section, settings, theme, site, page, entry, forloop and editing, plus link and collection objects.

A template can read these globals, and nothing else besides the variables it makes itself with assign, capture and for. They are read-only, and they are the same inside snippets.

GlobalWhat it is
sectionThe section being rendered: its id, handle, type, position and settings
settingsThe theme settings (config/settings_schema.json)
themeColours, fonts and style options chosen in the editor's Theme tab
siteBusiness name, WhatsApp number, language and language list
pageThe page being shown
entryThe item of a detail page, or nil
forloopInside a for loop
editingtrue only inside the editor

A missing property is nil and prints nothing, so you can't break a page by reading something that isn't there.

section

PropertyValue
section.idThe section's unique id, short lowercase letters and digits such as k3j9x1. Handy for unique HTML ids: id="menu-{{ section.id }}"
section.handleThe file name without .plang: promo-banner for sections/promo-banner.plang
section.typecode for a section that is added from the library, or the built-in type (hero, header, faq...) when your file replaces a built-in section
section.indexThe section's position on the page, counting from 0. The header and footer are rendered on their own, so they get 0
section.settingsThe values of the settings declared in the section's {% schema %}, by setting id

Most templates start by shortening the settings:

plang
{%- assign s = section.settings -%}
<h2>{{ s.heading }}</h2>

How each setting type arrives

Setting types are declared in the {% schema %} (see sections and settings). What the template receives depends on the type:

Schema typeYou getNotes
text, textarea, richtextText'' when empty. Output is escaped. For paragraphs use the paragraphs or lines filters, or markdown
select, radioThe chosen option's value as text
iconAn icon name such as TruckDraw it with icon: {{ s.icon | icon }}
number, rangeA number0 when not a valid number
checkbox, toggletrue or false
colorA hex colour like #D62828, or ''Only valid #rgb or #rrggbb values come through
image, image_pickerA safe image address (/uploads/... or https://...), or ''Anything else is dropped. Use the image snippet to get a placeholder when empty
link, urlA link object, or '' when emptyPrints as the address
collectionA collection object, or nilUp to 48 visible items
listA list of objects, one per item, with the item fields as propertiesAt most the list's max items. Each item field arrives by the rules in this table

Settings the schema doesn't describe (for example the style options every section gets: bg, pad, anchor) are copied as plain values. The platform already applies those to the section wrapper, so you rarely need them.

plang
{%- assign s = section.settings -%}
{% for plan in s.plans %}
  <h3>{{ plan.name }}</h3>
  {% if plan.price > 0 %}{{ plan.price | money }}{% endif %}
  {% render 'button', label: plan.button_label, link: plan.button_link %}
{% endfor %}

settings

settings holds the theme settings: the values behind config/settings_schema.json, which owners change in the editor's Theme tab. It is not the same as section.settings. A value is the owner's choice, or the schema's default if they haven't changed it.

json
[
  {
    "name": "Accent",
    "settings": [
      { "type": "color", "id": "accent_color", "label": "Accent colour", "default": "#D62828" },
      { "type": "checkbox", "id": "show_top_bar", "label": "Show top bar", "default": true }
    ]
  }
]
plang
{% if settings.show_top_bar %}<div class="top-bar">{{ site.name }}</div>{% endif %}
<span style="color: {{ settings.accent_color }}">Promo</span>

The same types as section settings work, except collection. Colours, numbers, ranges, selects and checkboxes are also written as CSS variables on the website, named --ts-<id> with _ turned into -. accent_color becomes --ts-accent-color. Ranges get their unit appended, and checkboxes become 1 or 0. See theme settings and styling.

theme

The choices made in the editor's Theme tab.

PropertyValue
theme.colors.brand, .accent, .base, .surface, .ink, .inverseThe six theme colours, as hex
theme.heading_font, theme.body_fontFont family names, such as Poppins
theme.heading_weight500, 600, 700 or 800 (as text)
theme.radiusnone, sm, md, lg or xl
theme.button_shapesquare, rounded or pill
theme.heading_casenormal or upper
theme.heading_styleplain or bar
theme.button_stylesolid, soft, outline or bold
theme.card_styleflat, border or shadow
theme.image_styleplain, arch or frame

Usually CSS variables are the better way to use the theme (var(--c-brand), see styling), because they follow each section's background. Use theme when the template itself has to decide something, such as {% if theme.button_shape == 'pill' %}.

site

PropertyValue
site.nameThe business name
site.whatsappThe WhatsApp number as digits in international format, such as 6281234567890. For a chat link use the whatsapp_url filter instead
site.langThe language code of the page being shown: id, en, ...
site.languagesThe language switcher: a list of { code, label, href, current }. label is the native name (English, 日本語), href is the same page in that language and current is true for the one being shown. The built-in header only shows a switcher when the list has more than one entry; do the same
site.homeThe address of the home page in the current language: / or /en
site.credittrue when the "made with Plangku" credit should be shown
site.app_urlThe address of Plangku itself, for the credit link
plang
{% if site.languages.size > 1 %}
  <nav aria-label="{{ 'language' | ui }}">
    {% for l in site.languages %}
      <a href="{{ l.href }}" hreflang="{{ l.code }}"{% if l.current %} aria-current="true"{% endif %}>{{ l.label }}</a>
    {% endfor %}
  </nav>
{% endif %}

page

The page being shown, or nil if there isn't one.

PropertyValue
page.titleThe page title
page.slugThe page's address part, '' for the home page
page.urlThe page's address in the current language, such as /tentang or /en/tentang

entry

On a detail page (the page that is shown for each item of a data collection) entry is the item being shown. On every other page it is nil, so test it first: {% if entry %}...{% endif %}. In the editor, a detail page shows a sample item so you can design it before real data exists.

Properties are listed on the data page: title, url, collection, images, image, price, old_price, badge, summary, excerpt, body, facts and fields.

forloop

Available inside {% for %}. See the full table on the language page: index, index0, rindex, rindex0, first, last, length and parentloop.

When you use {% render 'card' for items as item %}, the snippet gets a smaller forloop with index, index0, first, last and length.

editing

true while the template is rendered inside the editor canvas, false on the live site and in previews. Use it to show a hint that visitors should never see:

plang
{% if s.products == nil and editing %}
  <p>Pilih koleksi produk di pengaturan section ini.</p>
{% endif %}

Keep the hint harmless: the markup that editing adds is the only difference between the editor and the live site.

A link setting (and the output of the link filter) is a link object. Written out, it is the finished address, so <a href="{{ s.button_link }}"> just works. Behind it is a value the owner picks in the editor: a page, a WhatsApp message, a phone number, an email address, a section anchor or any web address.

PropertyValue
{{ link }} and link.hrefThe address to put in href
link.kindWhat the owner chose: url, whatsapp, page, anchor, phone or email
link.externaltrue for WhatsApp chats and for web addresses that start with http:// or https://, so you know to open a new tab

What each kind turns into:

Stored valuekindAddress written out
wa:Halo, saya mau pesanwhatsapphttps://wa.me/6281234567890?text=Halo%2C%20saya%20mau%20pesan (the site's number)
page:tentangpage/tentang (or /en/tentang on the English version). page: alone is the home page
#kontakanchor#kontak
tel:0812-3456-7890phonetel:081234567890
mailto:[email protected]emailmailto:[email protected]
https://tokopedia.com/kopiurlhttps://tokopedia.com/kopi
/produkurl/produk on this website

An empty link setting is the empty text '', which is false. Anything that can't be understood becomes #.

plang
{%- assign to = s.button_link -%}
<a href="{{ to | default: '#' }}"
   {%- if to.external %} target="_blank" rel="noopener noreferrer"{% endif %}>
  {%- if to.kind == 'whatsapp' %}{{ 'whatsapp' | icon: class: 'size-5' }}{% endif -%}
  {{ s.button_label }}
</a>

You rarely write this by hand: {% render 'button', label: ..., link: ... %} does exactly this (see snippets). The link filter makes a link object from any stored value, such as a text you built yourself: {% assign order = 'wa:Halo, mau pesan ' | append: p.title | link %}.

Collection objects

A collection setting gives a collection object, which lists a data collection's items (Produk, Menu, Artikel...). It is nil until the owner picks one.

PropertyValue
collection.idThe collection's id
collection.nameIts name, such as Produk
collection.slugIts address part, such as produk
collection.urlThe address of its list page, such as /produk
collection.itemsA list of at most 48 items, in the order set in the Data tab. Hidden (draft) items are left out

Each item has id, title, url, image, price, old_price, badge, subtitle, summary and fields. The data page explains them with a product grid example.

plang
{% for p in s.products.items limit: 8 %}
  <a href="{{ p.url | default: '#' }}">{{ p.title }} {{ p.price }}</a>
{% endfor %}