> ## Documentation Index
> Fetch the complete documentation index at: https://docs.instructorphp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 04 templates

# Templates

When prompt content is mostly markup — instructions, rubrics, output format specifications — a Twig template is easier to maintain than a PHP string. Xprompt integrates with the `packages/templates` engine so you can keep your `.twig` files next to your prompt classes.

## Template-Backed Prompt

Set `$templateFile` and `$templateDir` on your prompt class:

```php theme={null}
use Cognesy\Xprompt\Prompt;

class Analyze extends Prompt
{
    public string $templateFile = 'analyze.twig';
    public ?string $templateDir = __DIR__ . '/templates';
}
// @doctest id="e2d0"
```

The template file `templates/analyze.twig`:

```twig theme={null}
You will analyze the provided {{ content_type }} for quality issues.

## Criteria
- Clarity and readability
- Logical consistency
- Completeness of arguments

## Input
{{ content }}
// @doctest id="2a5b"
```

Render it like any other prompt:

```php theme={null}
echo Analyze::with(content_type: 'essay', content: $essay);
// @doctest id="f151"
```

Context variables map directly to Twig variables. The `body()` method you inherit handles loading, parsing, and rendering automatically.

## Colocating Templates

The recommended layout places templates alongside the prompt classes that use them:

```
src/Prompts/
    Analyze.php
    Summarize.php
    templates/
        analyze.twig
        summarize.twig
// @doctest id="6b6b"
```

Each prompt sets `$templateDir = __DIR__ . '/templates'`, keeping paths relative and portable.

## Front Matter

Templates can include YAML front matter for metadata. The front matter is parsed but not rendered — it's available via the `meta()` method:

```twig theme={null}
---
description: Analyze content for quality
model: sonnet
version: v2
---
Analyze the following {{ topic }} in detail.

Focus on {{ aspect }}.
// @doctest id="7a1b"
```

```php theme={null}
$prompt = Analyze::make();
$meta = $prompt->meta();
// ['description' => 'Analyze content for quality', 'model' => 'sonnet', 'version' => 'v2']
// @doctest id="63c4"
```

This is useful for storing prompt metadata — suggested model, version, author — alongside the prompt content. Your application code can read `meta()` to make decisions about which model to use or to track prompt versions.

## Introspection

Template-backed prompts expose their variables and validation state:

```php theme={null}
$prompt = Analyze::make();

// List all template variables
$prompt->variables();
// ['content_type', 'content']

// Check for missing or extra variables
$prompt->validationErrors(content_type: 'code');
// ['Missing variable: content']
// @doctest id="e341"
```

## Blocks

Blocks let you inject pre-rendered prompt content into template variables. Define block classes, list them in `$blocks`, and reference them in your template as `{{ blocks.ClassName }}`:

```php theme={null}
class Header extends Prompt
{
    public bool $isBlock = true;

    public function body(mixed ...$ctx): string
    {
        return "# {$ctx['title']}";
    }
}

class Footer extends Prompt
{
    public bool $isBlock = true;

    public function body(mixed ...$ctx): string
    {
        return "---\nEnd of document.";
    }
}

class Document extends Prompt
{
    public string $templateFile = 'document.twig';
    public ?string $templateDir = __DIR__ . '/templates';
    public array $blocks = [Header::class, Footer::class];
}
// @doctest id="7060"
```

The template `document.twig`:

```twig theme={null}
{{ blocks.Header }}

## Content
{{ body_text }}

{{ blocks.Footer }}
// @doctest id="ce32"
```

Blocks are rendered before the template, receiving the same context as the parent. The `isBlock = true` flag hides them from registry listings — they're internal building blocks, not standalone prompts.

## Overriding body()

If you need to add logic around template rendering, override `body()` and call `renderTemplate()` yourself, or compose the template prompt with other elements:

```php theme={null}
class SmartAnalyze extends Prompt
{
    public function body(mixed ...$ctx): array
    {
        return [
            Persona::with(role: 'analyst'),
            AnalyzeTemplate::with(topic: $ctx['topic']),
            $ctx['examples'] ? Examples::with(items: $ctx['examples']) : null,
        ];
    }
}
// @doctest id="be11"
```

## Next Steps

* [Structured Data](05-structured-data) — render lists and rubrics with NodeSet
* [Variants & Registry](06-variants-and-registry) — swap template-backed prompts without changing callers
