> For the complete documentation index, see [llms.txt](https://docs.uichemy.com/llms.txt).
> Use it to discover all available pages before exploring further.

# Dynamic Data With Twig

Use Twig expressions inside a Composer widget's HTML panel to output post data, apply filters, write conditions, and access site-wide values.


Twig is the expression language Composer uses for dynamic data inside a Composer widget's HTML panel. Write `{{ post.title }}` anywhere in the HTML and Composer replaces it with the current post's title when the page renders. No PHP, no shortcodes, no custom fields plugin required for standard WordPress data.

<Callout kind="info">
This is a UiChemy-built engine that uses Twig's syntax, not the full Twig library. Everything on this page, output expressions, filters, `{% if %}`, `{% for %}`, works as described, but there's no template inheritance (`{% extends %}`), no `{% include %}`, and no macros.
</Callout>

### Where to Write Twig

Open a Composer widget in Developer Mode (Style tab → Edit Code) and write Twig expressions directly in the HTML panel. Twig is processed server-side before the HTML reaches the browser.

```html
<article>
  <h1>{{ post.title }}</h1>
  <p class="date">{{ post.date|date('F j, Y') }}</p>
  <div class="excerpt">{{ post.excerpt }}</div>
  <a href="{{ post.link }}">Read More</a>
</article>
```

### Output Expressions: `{{ … }}`

Use double curly braces to output a value:

```twig
{{ post.title }}
{{ user.name }}
{{ site.name }}
{{ post.meta('price') }}
```

Values are HTML-escaped automatically. To output raw HTML (like post content that contains markup), use the `raw` filter: `{{ post.content|raw }}`.

### The Seven Data Providers

<Callout kind="warning">
The free version only resolves `post.title`, `post.content`, `post.link` / `post.url` / `post.permalink`, and `post.thumbnail` / `post.featured_image`, plus the full image chain (`src`, `alt`, `width`, `height`). All other post fields (`post.excerpt`, `post.date`, `post.author`, `post.categories`, `post.meta()`, etc.) and the entire `site`, `user`, `term`, and `request` providers require UiChemy Pro and return empty on Free.
</Callout>

**`post`: Current Post (partial Free)**

```twig
{{ post.title }}           {# Post title #}
{{ post.id }}              {# Post ID #}
{{ post.excerpt }}         {# Excerpt #}
{{ post.content|raw }}     {# Rendered content #}
{{ post.date }}            {# Published date (raw) #}
{{ post.link }}            {# Permalink #}
{{ post.slug }}            {# Post slug #}
{{ post.status }}          {# Post status #}
{{ post.comment_count }}   {# Number of comments #}
{{ post.meta('key') }}     {# Custom field value #}

{# Chained, author object #}
{{ post.author.name }}
{{ post.author.email }}
{{ post.author.avatar('96') }}

{# Chained, featured image #}
{{ post.thumbnail.src }}
{{ post.thumbnail.src('large') }}
{{ post.thumbnail.alt }}

{# Chained, first category #}
{{ post.categories.name }}
```

**`product`: WooCommerce Product (extends `post`)**

```twig
{{ product.price }}              {# Formatted price HTML (use |raw) #}
{{ product.regular_price }}
{{ product.sale_price }}
{{ product.is_on_sale }}         {# Boolean #}
{{ product.sku }}
{{ product.stock_status }}
{{ product.stock_quantity }}
{{ product.is_in_stock }}        {# Boolean #}
{{ product.add_to_cart_url }}
{{ product.average_rating }}
{{ product.review_count }}
{{ product.short_description|raw }}
{{ product.description|raw }}
{{ product.slug }}
{{ product.link }}
{{ product.thumbnail.src }}
```

**`user`: Current Logged-in User (Pro)**

```twig
{{ user.name }}          {# Display name #}
{{ user.id }}
{{ user.email }}         {# Admin context only #}
{{ user.bio|raw }}       {# Biography (HTML) #}
{{ user.url }}           {# Website URL #}
{{ user.link }}          {# Author archive URL #}
{{ user.avatar('64') }}  {# Avatar URL at given size in px #}
{{ user.meta('key') }}   {# User meta value #}
```

**`term`: Current Term (Pro)**

```twig
{{ term.name }}
{{ term.id }}
{{ term.slug }}
{{ term.description }}
{{ term.count }}         {# Number of posts #}
{{ term.link }}          {# Archive URL #}
```

**`image`: Image Object.** Not used directly, it's returned by chained expressions like `post.thumbnail`:

```twig
{{ post.thumbnail.src }}
{{ post.thumbnail.src('thumbnail') }}
{{ post.thumbnail.src('medium') }}
{{ post.thumbnail.src('large') }}
{{ post.thumbnail.src('full') }}
{{ post.thumbnail.alt }}
{{ post.thumbnail.width }}
{{ post.thumbnail.height }}
{{ post.thumbnail.caption }}
```

**`site`: Site-Wide Data (Pro)**

```twig
{{ site.name }}
{{ site.description }}   {# Tagline #}
{{ site.url }}
{{ site.language }}
{{ site.logo.src }}      {# Site logo image #}
{{ site.icon.src }}      {# Site icon (favicon) #}
{{ site.option('key') }} {# wp_options value #}
```

**`request`: URL Parameters (Pro)**

```twig
{{ request.get('tab') }}   {# ?tab=value from URL #}
{{ request.url }}          {# Current page URL #}
{{ request.referrer }}     {# HTTP referrer #}
```

### Filters: Transforming Values

Apply a filter with `|filtername`. Chain multiple filters with `|filter1|filter2`.

**String filters:**

```twig
{{ post.title|upper }}                   {# ALL CAPS #}
{{ post.title|lower }}                   {# all lowercase #}
{{ post.title|capitalize }}              {# First letter capitalized #}
{{ post.title|title }}                   {# Title Case #}
{{ post.excerpt|trim }}                  {# Remove leading/trailing whitespace #}
{{ post.excerpt|truncate(120) }}         {# Max 120 characters, ends with … #}
{{ post.excerpt|truncate(120, '...') }}  {# Custom suffix #}
{{ post.excerpt|excerpt(25) }}           {# Max 25 words #}
{{ post.content|striptags }}             {# Remove HTML tags #}
{{ post.title|slug }}                    {# url-safe-slug #}
{{ post.title|replace('Old', 'New') }}   {# Find and replace #}
{{ post.title|split(' ') }}              {# Split into an array #}
{{ post.title|format('Post: %s') }}      {# sprintf-style formatting #}
{{ value|default('Fallback text') }}     {# Show fallback if empty #}
{{ post.content|wpautop|raw }}           {# Add paragraph tags (like WordPress) #}
{{ post.content|shortcodes|raw }}        {# Render WordPress shortcodes #}
{{ post.content|kses_post|raw }}         {# Sanitize to allowed post HTML #}
{{ post.content|raw }}                   {# Output raw HTML #}
{{ post.content|e }}                     {# HTML-escape explicitly #}
{{ value|esc_attr }}                     {# Escape for an HTML attribute #}
{{ url|esc_url }}                        {# Escape and validate a URL #}
{{ post.excerpt|nl2br|raw }}             {# Convert line breaks to <br> #}
{{ data|json_encode|raw }}               {# Encode a value as JSON #}
```

**Number filters:**

```twig
{{ product.regular_price|number_format(2) }}  {# 1,299.00 #}
{{ value|round(2) }}                           {# Round to 2 decimal places #}
{{ value|abs }}                                {# Absolute value #}
{{ product.price|currency|raw }}               {# WooCommerce price format #}
```

**Date filters:**

```twig
{{ post.date|date('F j, Y') }}      {# January 15, 2025 #}
{{ post.date|date('d/m/Y') }}       {# 15/01/2025 #}
{{ post.date|date('M Y') }}         {# Jan 2025 #}
{{ post.date|time_ago }}            {# "3 days ago" #}
```

**Array filters** (for multi-value fields like categories or gallery):

```twig
{{ post.categories|first }}            {# First item #}
{{ post.categories|last }}             {# Last item #}
{{ post.categories|length }}           {# Count of items #}
{{ post.categories|reverse }}          {# Reversed array #}
{{ post.categories|sort }}             {# Sorted array #}
{{ post.categories|keys }}             {# Array keys #}
{{ post.categories.name|join(', ') }}  {# "News, Updates, Reviews" #}
{{ post.categories|map(c => c.name) }} {# Transform each item #}
{{ post.categories|filter(c => c.count > 0) }} {# Keep matching items #}
{{ post.categories|find(c => c.slug == 'news') }} {# First match #}
```

<Callout kind="tip">
`map`, `filter`, and `find` accept an arrow-function argument (`item => item.field`), a small non-standard extension for working with arrays of objects.
</Callout>

### Conditions: `{% if %}`

Show content conditionally:

```twig
{% if post.thumbnail %}
  <img src="{{ post.thumbnail.src('large') }}" alt="{{ post.thumbnail.alt }}">
{% endif %}

{% if user.id %}
  <p>Welcome, {{ user.name }}!</p>
{% else %}
  <a href="/login">Log in</a>
{% endif %}

{% if product.is_on_sale %}
  <span class="badge">Sale</span>
{% endif %}
```

### Structural Tags

Five `<uichemy-*>` tags render whole WordPress blocks. Place them directly in the HTML panel:

```html
<header>
  <uichemy-site-logo></uichemy-site-logo>
  <nav><uichemy-nav-menu></uichemy-nav-menu></nav>
</header>

<main>
  <uichemy-post-content></uichemy-post-content>
  <uichemy-toc></uichemy-toc>
</main>
```

| Tag | What it outputs |
|---|---|
| `<uichemy-nav-menu>` | Active WordPress navigation menu |
| `<uichemy-site-logo>` | Site logo linked to home |
| `<uichemy-site-icon>` | Site icon linked to home |
| `<uichemy-post-content>` | Current post content |
| `<uichemy-toc>` | Table of contents from post headings |

### Use Cases

- **Formatting a value inline**, dates, capitalization, or truncated excerpts, with a filter instead of a dynamic tag sub-option.
- **Showing or hiding content conditionally**, for example, only rendering a "Sale" badge when a product is on sale.
- **Chaining across providers**, drilling from a post to its author, or a product to a related term, in one expression.

### Frequently Asked Questions

<ExpandableGroup>
  <Expandable title="Twig runs server-side, does it affect page speed?" default-open="false">
    Twig expressions are evaluated once per page request and cached in the rendered HTML. The overhead is minimal, comparable to a WordPress template tag.
  </Expandable>

  <Expandable title="Can I use `{{ post.title }}` inside a CSS value?" default-open="false">
    Yes, in some contexts, for example a dynamic CSS custom property: `--bg-url: url('{{ post.thumbnail.src }}');`. Use `|esc_url` to ensure the URL is safe.
  </Expandable>

  <Expandable title="Can I access ACF fields through Twig?" default-open="false">
    ACF fields are available through `{{ post.meta('field_name') }}` (using the ACF field name or key). For complex ACF field types (gallery, repeater), use the ACF dynamic tags in the panel instead. See [ACF](/uichemy-composer/design-data/acf).
  </Expandable>

  <Expandable title="Is the |raw filter safe?" default-open="false">
    Only use `|raw` on values you trust (WordPress-generated content like `post.content|raw`). Never use `|raw` on user-input values.
  </Expandable>
</ExpandableGroup>

### Related

<Columns cols={2}>
  <Card title="Dynamic Tags" href="/uichemy-composer/design-data/dynamic-tags">
    Bind the same data with no code instead.
  </Card>
  <Card title="Loops" href="/uichemy-composer/design-data/loops">
    Use Twig's `{% for %}` loop to build dynamic listings.
  </Card>
  <Card title="Developer Mode" href="/uichemy-composer/build/developer-mode">
    Write Twig expressions in the panel where they actually go.
  </Card>
  <Card title="Dynamic Content Overview" href="/uichemy-composer/design-data/overview">
    See how Twig fits among the other data options.
  </Card>
</Columns>
