Directives Reference
Complete guide to all built-in APTL directives.
Control Flow
@if / @elif / @else
Conditional rendering based on expressions.
The @if directive allows you to conditionally include content based on expressions. You can use the block syntax for multi-line content, wrapping it between @if and @end. For simple conditions with single-line content, the inline syntax using a colon (:) provides a more compact alternative.
@if expression
Content when true
@end
@if expression
Content when true
@else
Content when false
@end
@if expression1
Content when expression1 is true
@elif expression2
Content when expression2 is true
@else
Content when all are false
@end
For simple one-liners, use the inline syntax:
@if expression: Single line content
Examples
Here’s a simple condition checking if a user is active:
@if user.isActive
Welcome back!
@end
The inline syntax is perfect for short messages:
@if isPremium: You have access to premium features
@if isAdmin: Administrator privileges enabled
@if user.hasNotifications: You have new notifications!
You can provide alternative content with @else:
@if user.isPremium
Premium features available
@else
Upgrade to access premium features
@end
Chain multiple conditions using @elif to check different cases:
@if userType == "admin"
Admin dashboard
@elif userType == "moderator"
Moderator panel
@elif userType == "user"
User profile
@else
Please log in
@end
Complex expressions can combine multiple conditions with logical operators:
@if user.age >= 18 and user.hasConsent
Full access granted
@end
@if status == "active" or status == "pending"
Account is processing
@end
@if not user.isBlocked
User is allowed
@end
Supported Operators
- Comparison:
==,!=,>,<,>=,<= - Logical:
and,or,not - Membership:
in - Grouping:
()
@each
Iterate over arrays and collections.
The @each directive lets you loop through arrays and render content for each item. You can access the item value and loop metadata through the automatically provided loop variable. Like other directives, it supports both block and inline syntax.
For basic iteration, specify the item name and the array path:
@each itemName in arrayPath
Content with @{itemName}
@end
For simple lists, the inline syntax provides a compact alternative:
@each itemName in arrayPath: Single line with @{itemName}
Examples
Here’s a simple iteration over an array of features:
@each feature in features
- @{feature.name}: @{feature.description}
@end
The inline syntax works great for generating compact lists:
@each item in items: • @{item.name} (@{item.price})
@each user in users: - @{user.name} <@{user.email}>
@each tag in tags: #@{tag}
Access the index through the loop variable:
@each user in users
@{loop.index}. @{user.name} (@{user.email})
@end
Loop Metadata
Within each iteration, APTL automatically provides a loop variable with useful metadata about the current iteration:
loop.index- The current iteration index (0-based)loop.first-trueif this is the first iterationloop.last-trueif this is the last iterationloop.even-trueif the index is evenloop.odd-trueif the index is oddloop.length- Total number of items being iterated
Examples using loop metadata:
@each user in users
@if loop.first
=== User List ===
@end
@{loop.index}. @{user.name}
@if loop.last
=== End of List ===
@end
@end
@each item in items
@if loop.even
<div class="even">@{item.name}</div>
@else
<div class="odd">@{item.name}</div>
@end
@end
@each task in tasks
Task @{loop.index + 1} of @{loop.length}: @{task.name}
@end
Loops can be nested to handle hierarchical data:
@each category in categories
## @{category.name}
@each item in category.items
- @{item.name}
@end
@end
Variables from the parent scope remain accessible within loops:
@each task in tasks
Task: @{task.name}
Owner: @{owner.name}
@end
Notes:
- The array path is resolved from the data context
- Item and index variables are scoped to the loop body
- Parent scope variables remain accessible
- Empty arrays produce no output
@switch / @case / @default
Switch-case conditional rendering for matching a value against multiple cases.
The @switch directive provides a clean way to handle multiple conditional branches based on a single value. It evaluates an expression and renders the content of the first matching @case, or the @default if no cases match.
@switch expression
@case value1
Content for case 1
@case value2
Content for case 2
@default
Default content
@end
Examples
Here’s a basic switch statement using string values to display different status messages:
@switch status
@case "pending"
⏳ Waiting for approval
@case "approved"
✅ Request approved
@case "rejected"
❌ Request rejected
@default
❓ Unknown status
@end
The switch directive works equally well with numeric values:
@switch priority
@case 1
🔴 Critical
@case 2
🟡 High
@case 3
🟢 Normal
@default
⚪ Low priority
@end
Case values can be variables that resolve at runtime:
@switch userRole
@case adminRole
Administrator access
@case moderatorRole
Moderator access
@default
User access
@end
You can nest other directives within case blocks:
@switch role
@case "admin"
@if isPremium
👑 Premium Administrator
@else
🔧 Administrator
@end
@case "user"
👤 Regular User
@default
👥 Guest
@end
Switch statements can be nested for more complex logic:
@switch category
@case "admin"
@switch level
@case 1
Super Admin
@case 2
Regular Admin
@default
Admin (Level @{level})
@end
@default
Regular User
@end
The switch directive works well within loops for processing collections:
@each task in tasks
@{task.name}: @switch task.status
@case "done"
✓ Complete
@case "in-progress"
⏳ In Progress
@case "todo"
☐ To Do
@end
@end
Features:
- Strict equality: Uses
===comparison (no type coercion) - First match wins: Only the first matching case is rendered
- Variable or literal values: Both switch expression and case values can be variables or literals
- Optional default: If no case matches and no
@defaultis provided, renders nothing - Nested variable paths: Supports
user.role,data[0].status, etc. - Supports all types: Strings, numbers, booleans, null, undefined
Supported Value Types:
- String literals:
@case "approved" - Numeric literals:
@case 42 - Boolean literals:
@case trueor@case false - Null/undefined:
@case nullor@case undefined - Variables:
@case someVariable - Nested paths:
@case user.role
Notes:
- Uses strict equality (
===) -"0"and0are different - The first matching case is rendered (subsequent matches are ignored)
- The
@defaultcase is optional - Only one case or the default is rendered per switch
- Case values can be literals or variables that resolve at runtime
Template Composition
@section
Define named sections for organization and template inheritance.
The @section directive is fundamental to organizing your templates. It creates named blocks of content that can be referenced, formatted, and overridden in template inheritance scenarios. Sections support both block and inline syntax, along with attributes that control rendering and inheritance behavior.
Use the block syntax for multi-line content:
@section name
Content
@end
@section "name with spaces"
Content
@end
@section name(attribute="value")
Content
@end
@section name attribute="value", another="value"
Content
@end
For simple one-liners like metadata or configuration values, the inline syntax is more concise:
@section name: Content goes here
@section title: AI Coding Assistant
@section version: 2.1.0
@section author: @{authorName}
Inline sections can also include attributes:
@section title(format="markdown"): # Welcome
@section config(role="system"): You are an assistant
Attributes
| Attribute | Type | Description |
|---|---|---|
role |
string | Section role (e.g., “system”, “user”) |
format |
string | Output format (“plain”, “markdown”, “json”, “structured”) |
override |
boolean | Override parent section completely |
prepend |
boolean | Prepend to parent section content |
append |
boolean | Append to parent section content |
overridable |
boolean | Allow children to override this section |
new |
boolean | Create new section (don’t inherit) |
Examples
Here’s a basic section defining an AI assistant’s identity:
@section identity
You are a helpful assistant.
@end
Inline sections are perfect for metadata and short content:
@section title: Code Review Assistant
@section version: 3.2.1
@section model: gpt-5
@section temperature: 0.7
You can mix block and inline syntax based on your content:
@section title: AI Coding Assistant
@section capabilities
- Code review
- Bug detection
- Refactoring suggestions
@end
@section author: @{authorName}
@section lastUpdated: @{timestamp}
The format attribute controls how section content is rendered:
@section code format="json"
{ "example": "data" }
@end
@section documentation format="markdown"
## Heading
Content here
@end
@section structure format="structured"
Main content
@section details
Detailed information
@end
@end
The structured format example above outputs:
<structure>
Main content
## Details
Detailed information
</structure>
Custom Section Titles
The title attribute allows you to override the section name in the output, or suppress the heading entirely:
@section first format="structured" title="Example"
content
@end
Output:
<first>
# Example
content
</first>
Set title=false to suppress the heading and prevent heading level increase for nested sections:
@section outer format="structured"
Outer content
@section middle format="markdown" title=false
Middle content
@section inner format="structured"
Inner content
@end
@end
@end
Output:
<outer>
Outer content
Middle content
## Inner
Inner content
</outer>
Notice that when title=false is used, the nested inner section becomes ## (level 2) instead of ### (level 3), because the middle section doesn’t contribute a heading level.
Mixed Format Example
You can mix different formatters for different sections. Here’s a powerful example combining structured and markdown formats:
@section api format="structured"
API Documentation
@section "Endpoint Details" format="markdown"
Use POST /api/users to create a new user
@section example format="json"
{ "name": "Alice", "role": "admin" }
@end
@end
@end
Output:
<api>
API Documentation
## Endpoint Details
Use POST /api/users to create a new user
### Example
{ "name": "Alice", "role": "admin" }
</api>
Template Inheritance
In template inheritance, you can completely replace a parent section with override:
@section header(override=true)
This replaces the parent's header
@end
Use prepend to add content before the parent’s content:
@section content(prepend=true)
This goes before the parent's content
@end
Or use append to add content after:
@section footer(append=true)
This goes after the parent's footer
@end
Mark sections as overridable in parent templates to allow child templates to modify them:
@section default(overridable=true)
Default content that children can override
@end
@extends
Inherit from a parent template.
The @extends directive enables template inheritance, allowing you to build upon a base template by overriding, prepending, or appending to its sections. This must be the first directive in your template.
@extends "parent-template.aptl"
Behavior:
- Must be the first directive in the template
- Loads the parent template
- Child sections can override, prepend, or append to parent sections
- Supports multi-level inheritance (grandchild → child → parent)
Example
Here’s a parent template that defines the base structure:
@section header(overridable=true)
Default Header
@end
@section content(overridable=true)
Default content
@end
@section footer
Copyright 2025
@end
A child template can extend it and customize specific sections:
@extends "base.aptl"
@section header(override=true)
Custom Header
@end
@section content(prepend=true)
Additional content before default
@end
This produces the following output:
Custom Header
Additional content before default
Default content
Copyright 2025
@include
Include another template inline.
The @include directive loads and renders another template at the current position. The included template has access to the same data context and can be used anywhere in your template, even multiple times.
@include "template-name.aptl"
Behavior:
- Loads and renders the specified template
- Included template has access to the current data context
- Can be used anywhere in a template
- Can be used multiple times
Example
Here’s a reusable header template:
@section header
# @{site.name}
@{site.tagline}
@end
You can include it in your main template:
@include "header.aptl"
@section content
Main content here
@end
Examples & Documentation
@examples
Define a set of few-shot examples for AI prompts.
The @examples directive creates a structured collection of example cases, particularly useful for few-shot prompting with AI models. Each example is defined using a @case directive within the @examples block.
@examples
@case argument="value"
@case argument="value"
@end
Example
Here’s how to define a set of code review examples:
@examples
@case input="Long function" output="Break into smaller functions"
@case input="Repeated code" output="Extract to reusable function"
@case input="Magic numbers" output="Use named constants"
@end
This renders in a structured format suitable for AI consumption:
Examples:
Input: Long function
Output: Break into smaller functions
Input: Repeated code
Output: Extract to reusable function
Input: Magic numbers
Output: Use named constants
@case
Define a single example case within an @examples block.
The @case directive (when used inside @examples) defines individual example cases with named parameters. This is different from the @case used in @switch statements.
@case param1="value1", param2="value2"
Parameters
The @case directive accepts named parameters. Common patterns include:
input- Example inputoutput- Expected outputscenario- Scenario descriptionresponse- Expected responseexplanation- Why this works
Examples
Basic input/output examples:
@examples
@case input="Code smell" output="Suggested fix"
@end
You can add explanations to provide additional context:
@examples
@case input="Example", output="Result", explanation="Because..."
@end
Directive Composition
Directives can be nested and combined to create complex template logic:
@section main
@if user.isActive
@each task in user.tasks
- @{task.name}
@if task.priority == "high"
⚠️ High priority
@end
@end
@end
@end
Custom Directives
APTL supports custom directives. See the API Reference for details on creating custom directives.