BoxLang is the AI-native software productivity platform for building, modernizing and running applications, with developers and AI agents working together.
Overview
BoxLang's templating language uses .bxm files (BoxLang Markup) to mix HTML with
dynamic server-side code. The default mode in .bxm files is HTML output — you
embed BoxLang logic using <bx:*> tags and output expressions with #expression#.
File Types Recap
| Extension | Default mode | Primary use |
|---|---|---|
.bx |
Script | Classes, services, models |
.bxs |
Script | Standalone scripts, CLI tools |
.bxm |
Tag/markup | Templates, views, HTML pages |
Output Expression Syntax
Use #expression# to interpolate values. The expression is evaluated and output
is HTML-escaped automatically inside <bx:output>:
<bx:output>
<h1>Hello, #user.name#!</h1>
<p>Today is #dateFormat( now(), "long" )#</p>
<p>Items: #order.items.len()#</p>
</bx:output>
Always wrap output in <bx:output> — without it, #variable# is treated
as a literal string and not evaluated:
<!-- BAD: #name# is printed as literal text, not evaluated -->
<p>#name#</p>
<!-- GOOD: #name# is evaluated and output -->
<bx:output><p>#name#</p></bx:output>
For reusable partial templates (app/views/partials/*.bxm), make <bx:output>
the wrapper around the rendered HTML block whenever interpolation appears in
that block. Keep bx:param and bx:set declarations outside the output block.
Core Template Components
<bx:output> — Render Dynamic Content
<bx:output>
<p>User: #user.firstName# #user.lastName#</p>
<p>Email: #encodeForHTML( user.email )#</p>
</bx:output>
Options:
encodeFor="html"— auto-encode all expressions (recommended for untrusted data)
<!-- Auto-encode all output in this block -->
<bx:output encodeFor="html">
<p>#userSuppliedContent#</p>
</bx:output>
<bx:set> — Declare Variables
<bx:set var="greeting" value="Hello, #user.name#!">
<bx:set var="items" value="#productService.getFeatured()#">
<bx:set var="count" value="#items.len()#">
<bx:if> / <bx:elseif> / <bx:else> — Conditionals
<bx:if condition="#user.isLoggedIn#">
<p>Welcome back, #encodeForHTML( user.name )#!</p>
<bx:elseif condition="#session.hasGuideToken#">
<p>Continue as guest</p>
<bx:else>
<a href="/login">Please log in</a>
</bx:if>
<bx:loop> — Iteration
Array loop:
<bx:loop array="#products#" item="product">
<div class="product">
<h3><bx:output>#encodeForHTML( product.name )#</bx:output></h3>
<p><bx:output>$#numberFormat( product.price, "0.00" )#</bx:output></p>
</div>
</bx:loop>
Struct loop:
<bx:loop struct="#config#" item="value" key="key">
<bx:output><p>#key#: #value#</p></bx:output>
</bx:loop>
Query loop:
<bx:loop query="#users#">
<bx:output>
<tr>
<td>#users.id#</td>
<td>#encodeForHTML( users.name )#</td>
<td>#encodeForHTML( users.email )#</td>
</tr>
</bx:output>
</bx:loop>
Index / count loop:
<bx:loop from="1" to="#items.len()#" index="i">
<bx:output><li>#i#. #items[ i ].name#</li></bx:output>
</bx:loop>
<bx:include> — Include Partial Templates
<!-- Reuse header and footer partials -->
<bx:include template="/includes/header.bxm">
<main>
<!-- page content -->
</main>
<bx:include template="/includes/footer.bxm">
Pass variables to the included template via the current scope (variables are inherited by default in includes).
<bx:script> — Inline Script Blocks
Switch to script mode within a template:
<bx:script>
var products = productService.getFeatured( limit=6 )
var totalPages = ceiling( products.recordCount / 10 )
</bx:script>
<bx:loop array="#products#" item="p">
<bx:output><div>#p.name#</div></bx:output>
</bx:loop>
<bx:switch> / <bx:case> — Switch Statements
<bx:switch expression="#user.role#">
<bx:case value="admin">
<bx:include template="/views/admin-dashboard.bxm">
</bx:case>
<bx:case value="editor">
<bx:include template="/views/editor-dashboard.bxm">
</bx:case>
<bx:defaultcase>
<bx:include template="/views/user-dashboard.bxm">
</bx:defaultcase>
</bx:switch>
<bx:try> / <bx:catch> — Error Handling in Templates
<bx:try>
<bx:set var="data" value="#remoteService.getData()#">
<bx:output><p>#data.message#</p></bx:output>
<bx:catch type="any" variable="e">
<p class="error">Could not load data: <bx:output>#encodeForHTML( e.message )#</bx:output></p>
</bx:catch>
</bx:try>
Mixing Script and Tag Syntax
You can freely mix script blocks and tag components in .bxm files:
<bx:script>
// Load data using script syntax
var pageTitle = "Product Catalog"
var categories = categoryService.getAll()
var featuredIds = url.keyExists( "cat" ) ? [ url.cat ] : []
</bx:script>
<!DOCTYPE html>
<html lang="en">
<head>
<bx:output><title>#encodeForHTML( pageTitle )#</title></bx:output>
</head>
<body>
<nav>
<bx:loop array="#categories#" item="cat">
<bx:output>
<a href="/catalog?cat=#encodeForURL( cat.slug )#"
class="#featuredIds.contains( cat.id ) ? 'active' : ''#">
#encodeForHTML( cat.name )#
</a>
</bx:output>
</bx:loop>
</nav>
</body>
</html>
Layout / View Patterns
Nested Includes (Layout Pattern)
<!-- views/layout.bxm -->
<!DOCTYPE html>
<html lang="en">
<head>
<bx:output><title>#pageTitle ?: "My App"#</title></bx:output>
<link rel="stylesheet" href="/css/app.css">
</head>
<body>
<bx:include template="/views/partials/nav.bxm">
<main>
<bx:include template="#contentTemplate#">
</main>
<bx:include template="/views/partials/footer.bxm">
</body>
</html>
<!-- In a page handler/template -->
<bx:set var="pageTitle" value="Dashboard">
<bx:set var="contentTemplate" value="/views/dashboard-content.bxm">
<bx:include template="/views/layout.bxm">
HTML Output Encoding Reference
Always encode user-supplied data before rendering:
<bx:output>
<!-- HTML context — encode entities -->
<p>#encodeForHTML( userBio )#</p>
<!-- Attribute context -->
<input value="#encodeForHTMLAttribute( formValue )#">
<!-- URL context -->
<a href="/profile?u=#encodeForURL( username )#">View Profile</a>
<!-- JavaScript context -->
<script>
const name = "#encodeForJavaScript( displayName )#";
</script>
</bx:output>
Query of Queries in Templates
Use <bx:query> for database queries directly in templates (acceptable for
simple templates; prefer service layer for complex logic):
<bx:script>
var users = queryExecute(
"SELECT id, name, email FROM users WHERE active = :active ORDER BY name",
{ active: { value: true, cfsqltype: "cf_sql_bit" } }
)
</bx:script>
<table>
<thead><tr><th>Name</th><th>Email</th></tr></thead>
<tbody>
<bx:loop query="#users#">
<bx:output>
<tr>
<td>#encodeForHTML( users.name )#</td>
<td>#encodeForHTML( users.email )#</td>
</tr>
</bx:output>
</bx:loop>
</tbody>
</table>
Common Mistakes to Avoid
| Mistake | Fix |
|---|---|
#var# outside <bx:output> |
Wrap in <bx:output> |
| Raw user data in output | Use encodeForHTML() or encodeFor="html" |
| Logic-heavy templates | Move to service layer; keep templates presentational |
| Missing closing tags | <bx:if> requires </bx:if>, <bx:loop> requires </bx:loop> |
| Unscoped variables | Declare with <bx:set> or <bx:script> var statements |
| Including with absolute OS paths | Use web-root-relative paths: /views/... |