Spintax Syntax Reference
Complete reference for spintax template markup.
Enumerations { }
Randomly selects one option from the list.
{option1|option2|option3}
Examples
{blue|grey|clear}
{|free|paid} plan ← empty option = sometimes nothing
{Acme {Pro|Lite}} ← nested enumerations
{order {|#42-A} confirmed} ← nesting with empty option
Rules
- Delimiters:
{and} - Separator:
| - Supports nesting to arbitrary depth
- Empty options are valid (produce empty string)
- Resolution is from the innermost expression outward
Permutations [ ]
Selects N elements, shuffles them, and joins with separators.
Simple permutations
All elements included, space-separated:
[1|2|3|4]
Output examples: 1 4 3 2, 2 3 4 1, 3 2 4 1
With separator
Uniform separator specified in < > at the start:
[<, > 1|2|3|4]
Output examples: 2, 1, 4, 3, 4, 3, 2, 1
Important: No space between [ and <separator>.
Per-element separators
Each option can have its own separator defined with <sep> before the preceding |. The separator travels with its element during shuffle.
[<, > 1|2|3 < and >|4]
Output examples: 1, 3, 2 and 4, 3, 1, 2 and 4
Auto-spacing: Word separators like <and> or <or> are automatically padded with spaces, so <and> produces and . Punctuation separators (<,>) are not padded.
Permutations with combinations
Configurable min/max element count and separators:
[<minsize=1;maxsize=3;sep=", ";lastsep=" and "> apple|plum|orange|apricot]
Output examples: apple, plum and orange, apple and apricot, orange
Configuration parameters
| Parameter | Default | Description |
|---|---|---|
minsize | count of all | Minimum number of elements to pick |
maxsize | count of all | Maximum number of elements to pick |
sep | " " (space) | Separator between non-final items |
lastsep | same as sep | Separator before the last element |
Permutation rules
- Delimiters:
[and] - Config block
<...>must immediately follow[ - Config parameters are semicolon-separated
- String values in config are quoted:
sep=", " - Enumerations and permutations can be nested inside options
- HTML elements can be options
Variables %var%
Defines a reusable variable that is substituted wherever it appears.
#set %VARIABLE_NAME% = value or spintax structure
#def %VARIABLE_NAME% = value or spintax structure
Examples
#set %name% = John
#set %greeting% = {Hello|Hi|Hey}
#set %items% = [<minsize=2;maxsize=3;sep=", ";lastsep=" and "> apples|oranges|bananas]
Some text with %name% and %greeting%, also %items%.
/# %greeting% above may differ between the two references — #set re-rolls.
#def picks once and keeps it: #/
#def %tone% = {friendly|warm|upbeat}
A %tone% intro, and a %tone% outro — always the same word.
Variable rules
#setand#defmust start at the beginning of a line- Variable names are enclosed in
%:%name% - Variable names are alphanumeric + underscore
- Values can contain any spintax syntax (enumerations, permutations, other variables)
#setvariables are expanded when referenced, not when defined (lazy evaluation)#setis a macro: its value is re-substituted — and any spintax inside it re-rolled — at every reference.#defresolves its value once per render and holds that result everywhere#setand#deflines are stripped from output
Variable scopes in the WordPress plugin
The plugin supports three variable scopes. When the same name exists in multiple scopes, the strongest scope wins:
- Runtime variables (strongest) — passed via shortcode:
[spintax slug="greeting" name="Alice"] - Local variables — defined with
#setor#definside the template - Global variables (weakest) — defined on the Settings page
Conditionals {?VAR?then|else}
Conditionals are spintax.net's distinctive extension to the GTW family. Where {a|b} is a uniform random pick that ignores variables, {?VAR?then|else} picks based on whether %VAR% has a value.
Use it for value-driven choices — show a free-tier line only when a free tier exists, render a pro-features block only when the user is on a paid plan, hide a CTA that does not apply.
The pre-pass runs before %var% expansion and before the random branch picker, so a falsy branch is fully discarded — nothing inside it is evaluated.
Forms
{?VAR?then} ← truthy ⇒ then; falsy ⇒ empty
{?VAR?then|else} ← truthy ⇒ then; falsy ⇒ else
{?!VAR?then|else} ← inverted
{?HasFreeTier? — free tier available since %founded%|, trusted since %founded%}
Truthy and falsy
The rule is deliberately simpler than JavaScript — truthy = at least one non-whitespace character:
Value of %VAR% | Truthy? |
|---|---|
| not declared | falsy |
| empty string | falsy |
| whitespace only | falsy |
"0", "false" | truthy (non-empty) |
| any other text or HTML | truthy |
Conditional rules
- Variable names follow the same regex as
%var%(case-insensitive) - The
!prefix inverts the check:{?!VAR?missing} - The first depth-0
|separatesthenfromelse; later top-level|stays literal inelse - Nested conditionals evaluate outer-first — falsy branches short-circuit
- Composite logic (
&&,||, comparisons) is not supported — pre-compute a guard variable in the assembler - Malformed forms (
{??yes},{?VAR}) never throw — the playground flags them as warnings - Deep dive: see the Conditional spintax guide for worked examples and anti-patterns
Plurals {plural %n%: one|many}
Picks the grammatically correct word form for a number. The count goes before the colon, the forms after it, separated by |.
The form is chosen by the render locale, not by the template — so how many forms you must supply depends on that locale. English needs two, Russian needs three.
{plural %n%: form1|form2} ← 2-form locale (en, de, es…)
{plural %n%: form1|form2|form3} ← 3-form locale (ru, uk, sr…)
#def %LangCount% = 5
supports %LangCount% {plural %LangCount%: language|languages}
← supports 5 languages
Forms per locale
The locale is matched on its language subtag, so ru-RU and ru behave identically:
| Locale | Forms | Selected by |
|---|---|---|
ru, uk, be, sr, hr, bs | 3 | 1 · 2–4 · 5 and up |
every other locale, incl. en | 2 | exactly 1 · everything else |
Supply the wrong number of forms and the engine reports plural.arity and leaves the block visible with fullwidth braces — a silent wrong plural never ships.
Plural rules
- The opener is literal, including the space:
{plural.{plural: x}and{pluralN: x}are not plural blocks - The colon is mandatory — it separates the count from the forms
- The count is a
%Var%reference or a literal integer; variables in the count are substituted before the form is chosen - Negative counts use the absolute value;
0takes the "everything else" form - A count variable must be
#def, not#set—#setis a macro, so a value like{1|4|9}is still unresolved spintax when the plural is decided and the whole block renders empty. The playground flags this asplural.count-macro - A non-numeric or undefined count erases the block rather than guessing
- Deep dive: see the Plural spintax guide for the Russian 3-form rules and worked examples
Includes #include
Embeds another template at the directive's position.
#include "hero-text"
Include rules
- Template reference is in double quotes
- Resolves by template slug or numeric ID
- Included templates can contain their own variables and spintax
- Recursive includes are supported
- Circular references are detected and blocked
- Child templates inherit global and runtime variables but not parent's
#set/#deflocals
Comments /#...#/
Text between comment markers is stripped from the output before any other processing.
/#
This is a comment section.
It can span multiple lines.
It won't appear in output.
#/
Comment rules
- Start delimiter:
/# - End delimiter:
#/ - Can span multiple lines
- Cannot be nested
- Removed before any other processing
Nesting
All syntax elements can be nested within each other to arbitrary depth:
{option1|[<, > sub1|sub2|sub3]|option3}
[<minsize=2;maxsize=3;sep=", ";lastsep=" and "> {red|blue} apples|{big|small} oranges|bananas]
#set %var% = {a|[b|c]}
Post-Processing
The engine applies automatic text correction after generation:
- Shield URLs, emails, domains, decimals, and abbreviations from capitalization
- Collapse duplicate spaces and tabs
- Remove whitespace before punctuation (
,.!?) - Add space after punctuation where missing
- Capitalize first letter of the output (skipping HTML tags)
- Capitalize after sentence-ending punctuation
- Capitalize after block-level HTML tags
- Capitalize after line breaks
- Restore shielded placeholders
Syntax Summary
| Feature | Syntax | Behavior |
|---|---|---|
| Enumeration | {a|b|c} | Pick one random option |
| Permutation | [a|b|c] | Pick N, shuffle, join |
| Separator | [<sep> a|b|c] | Permutation with uniform separator |
| Per-element sep | [<,> a|b <x>|c] | Permutation with custom separators |
| Combinations | [<config> a|b|c] | Permutation with min/max count |
| Variable | #set %var% = val | Reusable substitution |
| Variable (roll once) | #def %var% = val | Resolved once per render |
| Conditional | {?VAR?then|else} | Render then if truthy, else if falsy |
| Plural | {plural %n%: one|many} | Agree the word form with the number, by locale |
| Include | #include "slug" | Embed another template |
| Comment | /#...#/ | Stripped from output |
The syntax is compatible with the Generating The Web (GTW) standard.