Skip to main content
TemplateDX combines Markdown and JSX for type-safe LLM prompt templates. This page covers the full syntax.

Basic structure

Every TemplateDX template is a .mdx file with optional frontmatter and a mix of Markdown and JSX:
---
title: My Prompt Template
description: A brief description
---

# Your prompt content here

You are a helpful assistant specializing in {props.domain}.

<If condition={props.showInstructions}>
## Instructions
{props.instructions}
</If>

Frontmatter

Frontmatter is optional metadata at the top of your file:
---
title: Customer Support Prompt
description: Template for customer support interactions
version: 1.0
---

Variables

Access dynamic data using curly braces:
{props.userName}           // Simple variable
{props.user.email}         // Nested property
{props.items[0]}           // Array access
A missing nested property renders as an empty string (there’s no error to guard against, so ?. and . behave identically). Learn more about variables →

Expressions

Evaluate expressions inline: arithmetic, comparison, and logical operators, property access, and registered filter calls:
{props.score * 2}
{props.score >= 90}
{props.items.length}
{upper(props.name)}
{join(props.tags, ", ")}
JavaScript method calls (props.name.toUpperCase()) and the ternary operator (a ? b : c) are not supported. Use a filter or an <If> tag instead. Learn more about expressions →

Control flow

Conditionals

Use <If>, <ElseIf>, and <Else> tags:
<If condition={props.userType === "premium"}>
  You have access to premium features.
</If>
<ElseIf condition={props.userType === "standard"}>
  You have access to standard features.
</ElseIf>
<Else>
  You have access to basic features.
</Else>

Loops

Use <ForEach> to iterate over arrays:
<ForEach arr={props.items}>
  {(item, index) => (
    <>
      {index + 1}. {item.name} - {item.description}
    </>
  )}
</ForEach>
Learn more about control flow →

Filters

Transform data with built-in filters. TemplateDX ships 10 filters: abs, capitalize, dump, join, lower, replace, round, truncate, upper, urlencode.
{upper(props.status)}              {/* ACTIVE */}
{lower(props.email)}               {/* user@example.com */}
{capitalize(props.name)}           {/* Note: only capitalizes first char — doesn't lowercase the rest */}
{truncate(props.content, 100)}     {/* First 100 chars + "..." */}
{join(props.tags, ", ")}           {/* tag1, tag2, tag3 */}
Learn more about filters →

Components

Create reusable template components:
import SystemRole from './system-role.mdx';
import Examples from './examples.mdx';

<SystemRole role="expert" domain={props.domain} />

## Task

{props.taskDescription}

<Examples data={props.examples} />
Learn more about components →

Raw output

Use <Raw> to emit the enclosed content as literal source text; expressions inside <Raw> are not evaluated. The plugin re-serializes its children through Markdown, so you get exactly what you wrote.
<Raw>
  {props.variableName}
</Raw>
Output: \{props.variableName} (literal text, not the value of props.variableName). TemplateDX re-serializes the Raw content through remark-stringify, which backslash-escapes { and <, so braces and angle brackets appear escaped in the output.

XML tags

Lowercase XML tags are preserved as-is in the output, making them ideal for prompt engineering patterns like <examples>, <context>, and <instructions>. A fixed allow-list of HTML tags (per supported-tags.ts) also passes through unchanged. Expressions inside these tags are evaluated normally.
<examples>
<example>What is 2+2? The answer is 4.</example>
<example>What is 3+3? The answer is 6.</example>
</examples>

Now answer: What is {props.a}+{props.b}?
Output:
<examples>
<example>What is 2+2? The answer is 4.</example>
<example>What is 3+3? The answer is 6.</example>
</examples>

Now answer: What is 5+5?
XML tags support attributes and nesting:
<context type="system">You are an expert assistant.</context>
<instructions>
<rule>Be concise</rule>
<rule>Cite sources</rule>
</instructions>
Only lowercase tags are treated as XML passthrough (along with a fixed allow-list of HTML tags). The only PascalCase tags TemplateDX registers as built-ins are If, ElseIf, Else, ForEach, and Raw. Tags like <User>, <System>, and <Assistant> are AgentMark message tags layered on top of TemplateDX, not engine built-ins. Any other PascalCase tag must be a registered TagPlugin or an imported component, or bundling throws Unsupported tag '<TagName>'. Variables and expressions inside passthrough tags are still evaluated normally.

Comments

Use JSX-style comments:
{/* This is a comment */}

{/**
  * Multi-line comment
  * for documentation
  */}

Fragments

Use fragments to group elements without adding markup:
<>
  First line
  Second line
</>

Markdown support

TemplateDX supports all standard Markdown:
# Heading 1
## Heading 2

**Bold text**
*Italic text*

- Bullet list
- Item 2

1. Numbered list
2. Item 2

[Link text](https://example.com)

`inline code`

\`\`\`javascript
// Code block
const x = 10;
\`\`\`

Whitespace

TemplateDX preserves whitespace in your templates:
Line 1
Line 2

Paragraph with blank line above

Escaping literal braces

To emit literal { or } in output, wrap the content in <Raw>:
<Raw>{`{literal-braces}`}</Raw>

Type safety

See Type safety for the canonical flow: npx agentmark generate-types emits AgentmarkTypes from your prompt schemas. That’s the codegen pipeline AgentMark actually uses at runtime. TemplateDX itself doesn’t evaluate JSDoc or TypeScript types; comments are stripped at bundle time (bundler.ts:150 removeComments).

Best practices

  1. Use descriptive variable names. Prefer props.customerName over props.n.
  2. Keep templates modular. Break large templates into components.
  3. Generate types. Run npx agentmark generate-types and pass the result to createAgentMarkClient<AgentmarkTypes>() for compile-time prop validation.
  4. Use conditionals wisely. Make prompts adapt to context.
  5. Use filters. Transform data at the template level instead of reshaping it in your application code.

Complete example

Here’s a full example combining multiple features:
---
title: Product Review Analysis
---

{/**
  * @typedef Props
  * @property {string} productName
  * @property {Array<{author: string, rating: number, comment: string}>} reviews
  * @property {string} analysisType
  */}

# Product Review Analysis for {props.productName}

You are an expert product analyst. Analyze the following customer reviews and provide insights.

## Reviews ({props.reviews.length} total)

<ForEach arr={props.reviews}>
  {(review, index) => (
    <>
      ### Review {index + 1}
      **Rating**: {review.rating}/5
      **Author**: {capitalize(review.author)}

      "{truncate(review.comment, 200)}"

      ---
    </>
  )}
</ForEach>

## Analysis Instructions

<If condition={props.analysisType === "sentiment"}>
  Focus on overall sentiment and emotional tone in the reviews.
</If>
<ElseIf condition={props.analysisType === "features"}>
  Identify the most mentioned product features and customer opinions about them.
</ElseIf>
<Else>
  Provide a comprehensive analysis covering sentiment, features, and improvement suggestions.
</Else>

Please provide your analysis in a structured format.

Next steps