Templating
Raison prompts use Handlebars syntax for dynamic content. This guide covers every feature available in prompt templates.
Variables
Insert a variable value with double curly braces:
Hello, {{userName}}!
You are assisting with a {{context}} inquiry.
Call render with matching keys:
await raison.render("PROMPT_ID", {
userName: "Alice",
context: "billing",
});
// → "Hello, Alice!\nYou are assisting with a billing inquiry."
If a variable is not provided, it renders as an empty string.
Nested Objects
Access nested properties with dot notation:
User: {{user.name}}
Email: {{user.email}}
Account tier: {{user.account.tier}}
await raison.render("PROMPT_ID", {
user: {
name: "Alice",
email: "alice@example.com",
account: { tier: "pro" },
},
});
Conditionals
Use {{#if}} to include content conditionally:
{{#if isPremium}}
You have access to all premium features.
{{else}}
Upgrade your plan to unlock premium features.
{{/if}}
await raison.render("PROMPT_ID", { isPremium: true });
Falsy values (false, null, undefined, 0, "", []) will take the {{else}} branch. An {{else}} block is optional.
Loops
Use {{#each}} to iterate over arrays:
Available tools:
{{#each tools}}
- {{this}}
{{/each}}
await raison.render("PROMPT_ID", {
tools: ["search", "calculator", "calendar"],
});
// → "Available tools:\n- search\n- calculator\n- calendar"
Access the current index with @index (zero-based):
{{#each steps}}
Step {{@index}}: {{this}}
{{/each}}
For arrays of objects, access properties directly:
{{#each messages}}
{{role}}: {{content}}
{{/each}}
await raison.render("PROMPT_ID", {
messages: [
{ role: "user", content: "Hello" },
{ role: "assistant", content: "Hi there!" },
],
});
Unescaped Output
By default, the Raison SDK compiles templates with noEscape: true — HTML special characters are never escaped. You do not need triple braces ({{{value}}}) for raw output. Standard {{value}} already outputs the value as-is.
Triple braces work but behave identically to double braces in the SDK.
Escaping Braces
To output a literal {{ in your prompt without it being treated as a variable, escape it with a backslash:
Use the syntax \{{variableName}} to insert a variable.
Renders as: Use the syntax {{variableName}} to insert a variable.
Custom Helpers
Helpers are functions you register once, then call from any prompt template. They extend Handlebars with your own logic.
Registering Helpers
Register helpers at application startup, before rendering any prompts:
import { Raison } from "raison";
// Register before creating Raison instances
Raison.registerHelper("uppercase", (str: string) => str.toUpperCase());
Raison.registerHelper("lowercase", (str: string) => str.toLowerCase());
Raison.registerHelper("json", (obj: unknown) => JSON.stringify(obj, null, 2));
Helpers are global — they apply to all Raison instances and all prompt renders in the process.
Calling Helpers in Templates
User: {{uppercase userName}}
Context: {{json metadata}}
Common Helper Examples
Date formatting:
Raison.registerHelper("formatDate", (date: string | Date) => {
return new Date(date).toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
});
});
Today is {{formatDate currentDate}}.
JSON formatting (for embedding structured context):
Raison.registerHelper("json", (obj: unknown) => JSON.stringify(obj, null, 2));
User context:
{{json userContext}}
Conditional helper (inline ternary):
Raison.registerHelper("ifEqual", function (a, b, options) {
return a === b ? options.fn(this) : options.inverse(this);
});
{{#ifEqual plan "enterprise"}}
You have enterprise-level access.
{{else}}
Contact sales to upgrade.
{{/ifEqual}}
Truncation:
Raison.registerHelper("truncate", (str: string, length: number) => {
if (str.length <= length) return str;
return str.slice(0, length) + "...";
});
Summary: {{truncate description 200}}
Template Compilation Errors
If a template is syntactically invalid (e.g., an unclosed block), render catches the error and returns the raw template content as a safe fallback. Your application continues working — it just receives the unrendered template text instead of a processed string.
{{#if condition
The above would fail to compile. render would return the raw string "{{#if condition" rather than throwing.
If you need to detect compilation failures, check the returned string against the raw prompt content retrieved via findOne.
Combining Features
Full example combining conditionals, loops, and helpers:
You are a {{role}} assistant for {{uppercase companyName}}.
{{#if userContext}}
User information:
{{json userContext}}
{{/if}}
{{#if conversationHistory.length}}
Previous conversation:
{{#each conversationHistory}}
{{role}}: {{content}}
{{/each}}
{{/if}}
Today is {{formatDate currentDate}}.
const prompt = await raison.render("PROMPT_ID", {
role: "customer support",
companyName: "acme corp",
userContext: { plan: "pro", joinedDate: "2024-01-15" },
conversationHistory: [
{ role: "user", content: "I need help with my invoice" },
],
currentDate: new Date().toISOString(),
});