Skip to main content
When you need to scale your prompting, writing a new prompt for every unique scenario is not sustainable. Prompt templates solve this by letting you define a single, reusable blueprint that you can dynamically populate with structured data from a JSON payload. Each variable field in your template maps directly to a key in your JSON input, giving you full control over the generated output.

Insert Values

The most fundamental pattern is inserting a dynamic value directly into your prompt. Use the {{.key}} syntax to reference a specific field from your JSON payload.

Input

{
  "name": "Alice",
  "topic": "refunds"
}

Template

You are a customer support agent.

Greet {{.name}} and help them with {{.topic}}.

Result

You are a customer support agent.

Greet Alice and help them with refunds.
The key name in your template must match the key name in your JSON payload exactly. If the JSON payload does not contain the specified key, the template engine safely renders <no value> instead of raising an error or failing the execution.

Conditional Logic

Not all instructions apply to every scenario. You can conditionally include or exclude sections of your prompt by using if, else, and end blocks. This ensures your model only receives instructions relevant to the current context, saving context window tokens and reducing model confusion.

Input

{
  "name": "Bob",
  "isPremium": true
}

Template

Hello {{.name}}.

{{if .isPremium}}
This customer has priority support.
Respond with the fastest available resolution.
{{else}}
This customer is on the standard plan.
Offer upgrade options when relevant.
{{end}}

Result

Hello Bob.

This customer has priority support.
Respond with the fastest available resolution.
While conditional blocks are ideal for boolean flags, they also serve as presence checks. The engine treats empty strings, empty lists, nil, and zero values as false, allowing you to safely handle optional user inputs.

Logical and Comparison Operators

You can build complex conditions using logical functions (and, or, not) and comparison operators (eq, ne, lt, le, gt, ge). These functions use a prefix syntax where the operator precedes the arguments.

Input

{
  "temperature": 0.85,
  "maxTokens": 2048,
  "stream": false
}

Template

{{if gt .temperature 0.8}}
Use a highly creative and diverse generation strategy.
{{end}}

{{if and (not .stream) (le .maxTokens 4096)}}
Process the response in a single optimized batch.
{{end}}

Result

Use a highly creative and diverse generation strategy.

Process the response in a single optimized batch.

Iterate Over Lists

When your input data contains an array of items, use the range block to repeat a prompt segment for each element. This is useful for passing batch tasks, search results, or user options directly into the prompt.

Input

{
  "tasks": [
    "password reset",
    "billing question",
    "account deletion"
  ]
}

Template

The user needs help with:

{{range .tasks}}
- {{.}}
{{end}}

Address each item individually.

Result

The user needs help with:

- password reset
- billing question
- account deletion

Address each item individually.
Within the scope of a range block, the dot notation {{.}} refers directly to the current item being processed.

Iterate Over Structured Lists

Lists often contain structured objects rather than simple text values. Within a range loop, you can access the fields of each individual object directly using dot notation.

Input

{
  "messages": [
    {
      "role": "user",
      "text": "How do I reset my password?"
    },
    {
      "role": "assistant",
      "text": "Click the forgot password link."
    }
  ]
}

Template

Conversation history:

{{range .messages}}
{{.role}}: {{.text}}
{{end}}

Write the next response.

Result

Conversation history:

user: How do I reset my password?
assistant: Click the forgot password link.

Write the next response.
This structure is highly effective for injecting multi-turn conversation histories, support tickets, search database results, and other complex data records.

Access Root and Parent Scope inside Loops

Within a range block, the context . is rebound to the active element. To reference variables from the root of the JSON payload inside a loop, prefix the path with $. to escape the local scope.

Input

{
  "customerName": "Eve",
  "recommendations": [
    "premium support plan",
    "annual subscription discount"
  ]
}

Template

Offer recommendations tailored for {{.customerName}}:

{{range .recommendations}}
Hey {{$.customerName}}, consider signing up for the {{.}}.
{{end}}

Result

Offer recommendations tailored for Eve:

Hey Eve, consider signing up for the premium support plan.
Hey Eve, consider signing up for the annual subscription discount.

Access Nested Data and Context Rebinding

JSON payloads frequently contain hierarchical, nested objects. You can navigate these deeper data structures directly within your prompt template by chaining keys with dot notation.

Input

{
  "user": {
    "name": "Carol",
    "status": {
      "tier": "gold"
    }
  }
}

Template

Customer: {{.user.name}}
Membership: {{.user.status.tier}}

Result

Customer: Carol
Membership: gold

Context Rebinding with with

When navigating deeply nested objects, chaining can become repetitive. The with block binds the context . to the specified object within its scope. If the target object is empty, optional, or absent, the with block can execute an optional else block.

Input

{
  "metadata": {
    "organization": {
      "name": "Acme Corp",
      "region": "US-East"
    }
  }
}

Template

{{with .metadata.organization}}
Company: {{.name}}
Region: {{.region}}
{{else}}
Organization metadata is unavailable.
{{end}}

Result

Company: Acme Corp
Region: US-East

Variables and Assignment

You can declare custom variables inside templates to store values for reuse or to simplify access inside nested scopes. Declare and assign a variable using the {{$name := value}} syntax.

Input

{
  "instructions": {
    "preferredLanguage": "Spanish"
  },
  "queries": [
    "How to reset password",
    "How to change email"
  ]
}

Template

{{$lang := .instructions.preferredLanguage}}

{{range .queries}}
Process the query: "{{.}}"
Translate the final response to {{$lang}}.
{{end}}

Result

Process the query: "How to reset password"
Translate the final response to Spanish.
Process the query: "How to change email"
Translate the final response to Spanish.

Index and Key Capture in Loops

When iterating over a list using range, you can capture the index of the element alongside its value.

Input

{
  "steps": [
    "Analyze the request",
    "Extract key metadata",
    "Generate the response"
  ]
}

Template

Follow these instructions in order:

{{range $index, $step := .steps}}
- Instruction {{$index}}: {{$step}}
{{end}}

Result

Follow these instructions in order:

- Instruction 0: Analyze the request
- Instruction 1: Extract key metadata
- Instruction 2: Generate the response

Comments

To document your template without passing instructions or meta-commentary to the language model, use template comments. Comments are completely omitted during the rendering process, saving model context tokens.

Input

{
  "name": "Alice"
}

Template

{{/* Instruct the model to greet the user warmly */}}
Greet the user {{.name}} and welcome them back.

Result

Greet the user Alice and welcome them back.

Whitespace Control

In prompt engineering, extra newlines and spaces can consume unnecessary tokens or affect the structural formatting of model instructions. By default, template tags leave surrounding spaces and newlines intact. You can trim adjacent whitespace and newlines by adding a hyphen - to the template tags.
  • {{- trims whitespace and newlines immediately preceding the tag.
  • -}} trims whitespace and newlines immediately following the tag.

Input

{
  "userName": "Frank",
  "hasBonus": true
}

Template

Hello {{.userName}},

{{if .hasBonus -}}
We have applied a bonus to your account.
{{- else -}}
Standard billing rules apply.
{{- end}}

Let us know if you need help.

Result

Hello Frank,

We have applied a bonus to your account.

Let us know if you need help.
By using {{- and -}}, the conditional logic blocks are cleanly evaluated without introducing unwanted empty lines or spaces in the final prompt.

Built-in Utility Functions

The template engine includes several built-in functions to manipulate and format data during prompt rendering.

Length Check (len)

Use the len function to retrieve the number of elements in a list, map, or the number of characters in a string.

Input

{
  "contextDocs": [
    "Doc A",
    "Doc B"
  ]
}

Template

You have {{len .contextDocs}} reference documents available.

Result

You have 2 reference documents available.

Direct Indexing (index)

Use the index function to retrieve a specific element from an array or map by passing the array or map followed by the index keys.

Input

{
  "exemplars": [
    "Example 1: Input -> Output",
    "Example 2: Input -> Output"
  ]
}

Template

Analyze the primary exemplar:
{{index .exemplars 0}}

Result

Analyze the primary exemplar:
Example 1: Input -> Output

String Formatting (printf)

Use the printf function to construct formatted strings using verbs (such as %s for strings, %d for integers, or %.2f for float precision).

Input

{
  "modelName": "px0-ultra",
  "confidence": 0.9876
}

Template

Model: {{printf "%s" .modelName}}
Confidence: {{printf "%.2f" .confidence}}

Result

Model: px0-ultra
Confidence: 0.99

Provide Default Values

To protect your prompts from incomplete or missing input fields, you can define fallback values using the default function. This prevents rendering empty spaces or generic text when a user or upstream system provides sparse data.

Input

{
  "tone": ""
}

Template

Use a {{default "helpful and professional" .tone}} tone.

Result

Use a helpful and professional tone.
This mechanism guarantees that your prompts remain reliable and fully structured even when handling optional parameters. You can apply defaults to preserve fallback instructions for system roles, tones, response languages, or formatting preferences.

Comprehensive Example

Real-world prompts often combine multiple design patterns to handle complex application logic. The following example demonstrates how value insertion, conditional blocks, loops with index capturing, root scope access, whitespace control, and default fallbacks work together to form a highly resilient and adaptable prompt template.

Input

{
  "system": {
    "role": "expert security auditor"
  },
  "userName": "Dana",
  "hasAccess": true,
  "confidenceThreshold": 0.85,
  "vulnerabilities": [
    {
      "id": "CVE-2026-0001",
      "severity": "critical",
      "description": "Remote code execution in core parser"
    },
    {
      "id": "CVE-2026-0002",
      "severity": "medium",
      "description": "Information disclosure in logging"
    }
  ]
}

Template

You are an {{default "assistant" .system.role}}.

{{if and .hasAccess (gt .confidenceThreshold 0.8) -}}
ACCESS GRANTED: Audit mode enabled for user {{.userName}}.
{{- else -}}
ACCESS DENIED: Insufficient confidence or credentials.
{{- end}}

Total vulnerability alerts found: {{len .vulnerabilities}}

{{range $index, $vuln := .vulnerabilities -}}
- Alert {{$index}}: [{{$vuln.id}}] Severity: {{$vuln.severity}}
  Description: {{$vuln.description}}
  Auditor role: {{$.system.role}}
{{end}}

Result

You are an expert security auditor.

ACCESS GRANTED: Audit mode enabled for user Dana.

Total vulnerability alerts found: 2

- Alert 0: [CVE-2026-0001] Severity: critical
  Description: Remote code execution in core parser
  Auditor role: expert security auditor
- Alert 1: [CVE-2026-0002] Severity: medium
  Description: Information disclosure in logging
  Auditor role: expert security auditor
By combining these building blocks, you can construct robust prompt templates that dynamically adapt to any incoming payload while maintaining consistent instructions for the language model.