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:
use Cognesy\Xprompt\Prompt;
class Analyze extends Prompt
{
public string $templateFile = 'analyze.twig';
public ?string $templateDir = __DIR__ . '/templates';
}
The template file templates/analyze.twig:
You will analyze the provided {{ content_type }} for quality issues.
## Criteria
- Clarity and readability
- Logical consistency
- Completeness of arguments
## Input
{{ content }}
Render it like any other prompt:
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:
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:
---
description: Analyze content for quality
model: sonnet
version: v2
---
Analyze the following {{ topic }} in detail.
Focus on {{ aspect }}.
$prompt = Analyze::make();
$meta = $prompt->meta();
// ['description' => 'Analyze content for quality', 'model' => 'sonnet', 'version' => 'v2']
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:
$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']
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 }}:
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];
}
The template document.twig:
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:
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,
];
}
}
Next Steps¶
- Structured Data — render lists and rubrics with NodeSet
- Variants & Registry — swap template-backed prompts without changing callers