> For the complete documentation index, see [llms.txt](https://docs.acpt.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.acpt.io/dynamic-blocks/twig-reference.md).

# TWIG reference

Complete reference for variables, functions, filters, and syntax available in dynamic block TWIG templates

Dynamic Block callbacks are rendered by [Symfony Twig](https://twig.symfony.com/). This page documents everything that ACPT injects into the TWIG environment automatically.

***

## Naming rules

Before diving into variables, there is one critical rule: **all variable names are lowercased and dashes are converted to underscores.**

This applies to:

* Block control names (`My Control` → `my_control`, `badge-label` → `badge_label`)
* ACPT meta box and field names (`My-Box` → `my_box`, `Field Name` → `field_name`)

If a variable doesn't resolve, check the name in your meta box/field settings and apply this rule.

***

## Global site variables

These variables are injected into every block template, regardless of post type or context.

| Variable           | Type   | Value                                            |
| ------------------ | ------ | ------------------------------------------------ |
| `current_year`     | string | Current 4-digit year, e.g. `2025`                |
| `site_name`        | string | WordPress site name (`get_bloginfo('name')`)     |
| `site_url`         | string | Site URL (`get_bloginfo('url')`)                 |
| `site_description` | string | Site tagline (`get_bloginfo('description')`)     |
| `site_logo`        | string | URL of the site logo, or empty string if not set |

**Example:**

```twig
<footer>
    <p>© {{ current_year }} {{ site_name }}</p>
</footer>
```

***

## ACPT field variables

Every ACPT meta field attached to any post type is automatically available as:

```
acpt_{box_name}_{field_name}
```

Both `box_name` and `field_name` follow the naming rules above (lowercase, dashes → underscores).

**Example:** A meta box named `Event Details` with a field named `Event Date` → `{{ acpt_event_details_event_date }}`

### Return types by field type

The value returned mirrors what `get_acpt_field()` returns for that field type:

| Field type                                  | TWIG type | Notes                                                                 |
| ------------------------------------------- | --------- | --------------------------------------------------------------------- |
| Text, Textarea, Email, URL, Phone, Password | `string`  | Plain text value                                                      |
| Number, Range                               | `string`  | Numeric value as a string                                             |
| Date, Time, Datetime                        | `string`  | Formatted date/time string                                            |
| Toggle, Checkbox                            | `boolean` | `true` or `false`                                                     |
| Select, Radio                               | `string`  | The selected option value                                             |
| Image                                       | `string`  | URL of the image                                                      |
| Gallery                                     | `array`   | Array of image URLs                                                   |
| File                                        | `string`  | URL of the file                                                       |
| Editor (WYSIWYG)                            | `string`  | HTML content — use `\|raw`                                            |
| Repeater                                    | `array`   | Array of row objects                                                  |
| Address                                     | `object`  | Object with `street`, `city`, `state`, `zip`, `country`, `lat`, `lng` |
| Currency                                    | `object`  | Object with `value` and `symbol`                                      |
| Length, Weight                              | `object`  | Object with `value` and `unit`                                        |

### Outputting a text field

```twig
<p>{{ acpt_event_details_location }}</p>
```

### Outputting an HTML field

```twig
<div class="description">
    {{ acpt_event_details_description|raw }}
</div>
```

### Looping over a gallery

```twig
<ul class="gallery">
    {% for image_url in acpt_product_info_gallery %}
        <li><img src="{{ image_url }}" alt="" /></li>
    {% endfor %}
</ul>
```

### Accessing an address object

```twig
{% set addr = acpt_property_details_address %}
{% if addr %}
    <address>
        {{ addr.street }}, {{ addr.city }}, {{ addr.state }} {{ addr.zip }}
    </address>
{% endif %}
```

### Looping over a repeater

```twig
{% for row in acpt_schedule_items_session %}
    <div class="session">
        <h3>{{ row.title }}</h3>
        <p>{{ row.speaker }} — {{ row.time }}</p>
    </div>
{% endfor %}
```

***

## Block control variables

When you add controls to a block (Step 3 of the wizard), each control's current value becomes a TWIG variable named after the control.

**Naming:** same rule as fields — lowercase, dashes/spaces → underscores.

| Control type                                | TWIG type | Example                                |
| ------------------------------------------- | --------- | -------------------------------------- |
| Text, Textarea, Email, Phone, Radio, Select | `string`  | `{{ my_text_control }}`                |
| Number, Range                               | `integer` | `{{ font_size }}`                      |
| Checkbox, Toggle                            | `boolean` | `{% if show_badge %}`                  |
| SelectMulti                                 | `array`   | `{% for val in selected_categories %}` |

**Example — conditional badge from a Toggle:**

```twig
{% if show_badge %}
    <span class="badge">{{ badge_label|default('New') }}</span>
{% endif %}
```

**Example — looping over a SelectMulti:**

```twig
{% for tag in selected_tags %}
    <span class="tag">{{ tag }}</span>
{% endfor %}
```

***

## WordPress functions

ACPT registers the following WordPress functions directly in the TWIG environment. Call them exactly as you would in PHP, but without the `echo`.

| Function          | Signature                             | Returns                             |
| ----------------- | ------------------------------------- | ----------------------------------- |
| `get_permalink`   | `get_permalink(id?)`                  | `string` — URL of the post          |
| `get_the_title`   | `get_the_title(id?)`                  | `string` — post title               |
| `get_the_content` | `get_the_content()`                   | `string` — post content HTML        |
| `get_the_excerpt` | `get_the_excerpt()`                   | `string` — post excerpt             |
| `get_post_meta`   | `get_post_meta(post_id, key, single)` | `mixed` — raw meta value            |
| `get_posts`       | `get_posts(args_array)`               | `array` — array of WP\_Post objects |
| `have_posts`      | `have_posts()`                        | `boolean`                           |
| `the_post`        | `the_post()`                          | `void` — advances the loop pointer  |
| `do_shortcode`    | `do_shortcode(content)`               | `string` — shortcode output HTML    |

**Example — link to the current post:**

```twig
<a href="{{ get_permalink() }}">Read more</a>
```

**Example — render an ACPT shortcode:**

```twig
{{ do_shortcode("[acpt box='product_info' field='price']")|raw }}
```

> Always use `|raw` with `do_shortcode` and `get_the_content` to output unescaped HTML.

**Example — query related posts:**

```twig
{% set related = get_posts({'post_type': 'event', 'posts_per_page': 3}) %}
{% for post in related %}
    <a href="{{ get_permalink(post.ID) }}">{{ get_the_title(post.ID) }}</a>
{% endfor %}
```

***

## TWIG filters

Twig's full filter library is available. The most commonly used ones in ACPT blocks:

| Filter       | Example                            | Description                                                                |
| ------------ | ---------------------------------- | -------------------------------------------------------------------------- |
| `raw`        | `{{ html_content\|raw }}`          | Output HTML without escaping — **required for HTML fields and shortcodes** |
| `default`    | `{{ title\|default('Untitled') }}` | Fallback value if variable is empty or undefined                           |
| `upper`      | `{{ label\|upper }}`               | Uppercase                                                                  |
| `lower`      | `{{ label\|lower }}`               | Lowercase                                                                  |
| `capitalize` | `{{ name\|capitalize }}`           | Capitalise first letter                                                    |
| `length`     | `{{ items\|length }}`              | Count array items or string length                                         |
| `date`       | `{{ date_string\|date('d/m/Y') }}` | Format a date string                                                       |
| `slice`      | `{{ gallery\|slice(0, 3) }}`       | Take a subset of an array                                                  |
| `join`       | `{{ tags\|join(', ') }}`           | Join array elements into a string                                          |
| `trim`       | `{{ text\|trim }}`                 | Remove leading/trailing whitespace                                         |
| `nl2br`      | `{{ textarea_value\|nl2br\|raw }}` | Convert newlines to `<br>` tags                                            |
| `striptags`  | `{{ html\|striptags }}`            | Strip HTML tags                                                            |

See the full list in the [Twig filter documentation](https://twig.symfony.com/doc/3.x/filters/index.html).

***

## Control flow

Standard Twig control-flow tags all work as expected.

### Conditionals

```twig
{% if acpt_event_details_date %}
    <p class="date">{{ acpt_event_details_date }}</p>
{% elseif acpt_event_details_time %}
    <p class="time">{{ acpt_event_details_time }}</p>
{% else %}
    <p class="no-date">Date TBD</p>
{% endif %}
```

### Loops

```twig
{% for image_url in acpt_product_info_gallery %}
    <img src="{{ image_url }}" alt="" />
{% else %}
    <p>No images available.</p>
{% endfor %}
```

> The `{% else %}` branch inside `{% for %}` renders when the array is empty.

### Setting variables

```twig
{% set price = acpt_product_info_price %}
{% set currency = acpt_product_info_currency %}
<p>{{ price }} {{ currency }}</p>
```

### Macros (reusable snippets)

```twig
{% macro badge(label, color) %}
    <span class="badge badge--{{ color }}">{{ label }}</span>
{% endmacro %}

{{ _self.badge('New', 'green') }}
{{ _self.badge('Sale', 'red') }}
```

***

## Error handling

If TWIG encounters a syntax error or an undefined variable (in strict mode), the block renders an empty string instead of throwing a PHP error. To debug, use the **Check Twig** button in the block callback editor — it validates syntax without saving.
