SLT Syntax
Salata processes .slt files -- plain text files with embedded runtime blocks. The format is intentionally simple: anything outside a runtime block passes through to the output unchanged, and anything inside a runtime block is executed server-side with its stdout captured and spliced into the output.
File Format
A .slt file is just text. It can contain HTML, JSON, YAML, plain prose, configuration syntax, or anything else. Salata does not parse or validate the surrounding text -- it only looks for runtime block tags.
<!DOCTYPE html>
<html>
<head><title>Hello</title></head>
<body>
<h1>Static heading</h1>
<python>
print("<p>This paragraph is generated by Python.</p>")
</python>
</body>
</html>
In this example, everything outside the <python>...</python> block is emitted as-is. The Python block executes, its print() output replaces the block, and the final result is a complete HTML page.
Runtime Blocks
Six runtime tags are available:
<python>...</python><ruby>...</ruby><javascript>...</javascript><typescript>...</typescript><php>...</php><shell>...</shell>
Each tag tells Salata to execute the enclosed code using the corresponding runtime. Whatever the code prints to stdout replaces the entire tag (opening tag, code, closing tag) in the output.
Today's date is: <shell>date +%Y-%m-%d</shell>
Output:
Today's date is: 2026-02-22
Pass-Through Tags
<style> and <script> tags are client-side -- Salata passes them through untouched. They are not runtime blocks and their contents are never executed server-side.
<style>
body { font-family: sans-serif; }
</style>
<script>
document.title = "Client-side JS";
</script>
This distinction matters: <script> is client-side JavaScript that runs in the browser, while <javascript> is server-side JavaScript executed by Node.js during template processing.
No Nesting
Runtime tags cannot be nested inside other runtime tags. This is a parse-time error:
<!-- THIS IS INVALID -->
<python>
print("<ruby>puts 'hello'</ruby>")
</python>
Salata will reject this file with a parse error before any code executes. If you need one runtime to influence another, use the #set/#get macro system for cross-runtime data sharing.
Execution Order
Blocks execute top-to-bottom, synchronously. Each block finishes before the next one starts. This means you can rely on ordering:
<python>
import time
print(f"<p>Started at {time.strftime('%H:%M:%S')}</p>")
</python>
<ruby>
puts "<p>Ruby runs after Python finishes.</p>"
</ruby>
Python always completes before Ruby begins.
Automatic Dedenting
Code inside runtime blocks is automatically dedented. Salata strips the common leading whitespace from all lines in a block, so you can indent your code naturally within the HTML structure without worrying about extra spaces being passed to the runtime:
<body>
<div class="content">
<python>
for i in range(3):
print(f"<p>Item {i}</p>")
</python>
</div>
</body>
The Python code is dedented before execution, so the for loop starts at column 0 from Python's perspective. This avoids IndentationError in Python and keeps your .slt files readable.
Encoding
UTF-8 is enforced everywhere -- input files, runtime output, and final output. All runtimes are invoked with UTF-8 encoding settings. Non-UTF-8 input will produce an error.
Output Is Not Restricted to HTML
While HTML is the most common use case, .slt files can produce any text format. The output is whatever the runtime blocks print, combined with the static text outside the blocks:
# Generated config
<python>
services = ["web", "api", "worker"]
for svc in services:
print(f"[service.{svc}]")
print(f"enabled = true")
print()
</python>
This produces a TOML configuration file, not HTML. Salata is format-agnostic.