Template Expressions
Use Handlebars templates to render dynamic text content in workflows. Call this.render() from any BaseWorkflow to interpolate state values, format prompts, and generate dynamic output.
Setup
import { BaseWorkflow, Transition, Workflow } from '@loopstack/common';
@Workflow({})export class MyWorkflow extends BaseWorkflow { // this.render() is available from BaseWorkflow — no injection needed}Usage
import { join } from 'node:path';
const rendered = this.render(join(__dirname, 'templates', 'prompt.md'), { subject: args.subject, items: state.items,});this.render() expects an absolute path. Build it with path.join(__dirname, ...) so templates resolve relative to the workflow’s compiled .js file at runtime (where the templates/ folder is copied during build).
Template file (templates/prompt.md):
Write a haiku about {{subject}}.
{{#each items}}
- {{this.name}} {{/each}}Passing Data
Pass any data as the second argument to this.render():
// Workflow args (from ctx parameter — typed via RunContext<{ subject: string }>)this.render(templatePath, { subject: ctx.args.subject });
// Workflow state (from state parameter)this.render(templatePath, { items: state.items, count: state.counter });
// Mixed datathis.render(templatePath, { prompt: ctx.args.prompt, history: state.conversationHistory, timestamp: new Date().toISOString(),});Handlebars Syntax
Variables
Hello {{name}}Nested: {{user.profile.email}}Array element: {{items.[0]}}Conditionals
{{#if isActive}}Welcome back!{{else}}Please log in{{/if}}{{#unless isBlocked}}Access granted{{/unless}}Iteration
{{#each items}}
- {{this.name}}: {{this.value}} {{else}} No items found. {{/each}}Context Scoping
{{#with user}}{{name}} ({{email}}){{/with}}Multi-line Template Example
# Events This Week
{{#each events}}
- **{{this.summary}}**: {{this.start}} – {{this.end}} {{/each}}
{{#unless events}}No events found.{{/unless}}When to Use Templates
| Scenario | Approach |
|---|---|
| LLM prompts with variables | this.render(templatePath, data) |
| Simple string interpolation | Template literals in TypeScript |
| Complex multi-line content | Handlebars template file |
| Prompts with iteration/conditionals | Handlebars with #each, #if |
YAML UI Config
YAML widget configuration uses transition values that reference method names and enabledWhen / showWhen to enable or show widgets only at certain places. These are not template expressions — they are static configuration:
ui: widgets: - widget: prompt-input enabledWhen: [waiting_for_user] options: transition: userMessageRegistry References
- Prompt example — Uses
this.render()for Handlebars prompt templates - Meeting Notes example — Renders the LLM prompt that extracts structured notes from a template