Stat Test Page
BookStack D&D 5e Statblock Guide
This guide documents the JSON and Tetra-cube .monster workflow for the BookStack statblock renderer. Install the renderer once in BookStack custom head content, then paste small HTML wrappers into any page. The wrapper contains JSON; the renderer turns that JSON into a styled D&D 5e monster statblock when the page loads.
Files
Main Renderer
bookstack-custom-html-with-json-statblocks.html
Contains the CSS and JavaScript that should live in BookStack custom HTML head content.
Paste Helper
bookstack-monster-paste-helper.html
Local helper page for loading, dropping, or pasting a .monster file and generating the wrapper HTML.
This Guide
bookstack-dnd-statblock-guide.html
Human-readable usage reference with examples you can copy into BookStack source mode.
Quick Start
This setup has two different pieces. The CSS and JavaScript are installed globally in BookStack once. The monster data is pasted on individual BookStack pages whenever you want a statblock.
bookstack-custom-html-with-json-statblocks.html.
Copy the statblock CSS and script into BookStack's Custom HTML Head Content area.
Open a BookStack page in source or HTML mode.
Paste a wrapper such as <div class="dnd-monster-data">...</div> with JSON inside it. Use dnd-monster-data for Tetra-cube .monster files and dnd-statblock-data for hand-authored JSON.
Save the page. The renderer hides the wrapper and replaces it with a styled D&D 5e statblock.
<template class="dnd-monster-data"> when BookStack allows it. Template content is naturally hidden before the renderer runs. If BookStack strips template, use the plain div wrapper instead.How It Works in BookStack
The renderer is intentionally simple: it does not need a database table, attachment lookup, iframe, remote API, or plugin. It only needs hidden JSON on the page. This makes it portable across pages and keeps the statblock data close to the lore, encounter note, or adventure text that uses it.
Install Once
Put the CSS and JavaScript from the main renderer file into BookStack custom head content. After that, every page can use statblocks.
Paste Per Page
Each monster lives in a wrapper element on the BookStack page. The wrapper is usually a template or div with a class and JSON inside.
Render on View
When someone views the page, the script finds the wrappers, parses the JSON, and replaces each wrapper with the finished statblock.
Fail Clearly
If JSON is broken, the page shows a visible error box with the parse message, line, column, and a short excerpt near the problem.
Where Each Piece Goes
.monster JSON
Inside a page wrapper such as <template class="dnd-monster-data">
Every time you want to add a monster to a page.
Display options
Attributes on the wrapper, such as data-layout="two-column"
Only when a statblock needs a different layout or helper behavior.
Rendered Statblock Examples
These are actual rendered statblocks, not code samples. They show the visual output you should expect after BookStack replaces the JSON wrapper on a page.
Standard Monster Output
A compact one-column creature with core stats, properties, and one action.
Lantern-Haunt Wight
Medium undead, lawful evil
Armor Class 15 (ancient mail)
Hit Points 67 (9d8 + 27)
Speed 30 ft.
Skills Perception +4, Stealth +3
Damage Resistances necrotic; bludgeoning, piercing, and slashing from nonmagical attacks
Senses darkvision 60 ft., passive Perception 14
Languages Common plus one language it knew in life
Challenge 4 (1,100 XP)
Actions
Graveblade. Melee Weapon Attack: +5 to hit, reach 5 ft., one target. Hit: 8 (1d10 + 3) slashing damage plus 7 (2d6) necrotic damage.
Social NPC Output
A statblock does not have to be only a combat block. This one foregrounds skills, traits, and a reaction.
Archivist of the Lower Stacks
Medium humanoid, lawful neutral
Armor Class 12
Hit Points 33 (6d8 + 6)
Speed 30 ft.
Saving Throws Int +5, Wis +4
Skills Arcana +5, History +7, Insight +4, Investigation +5
Senses passive Perception 12
Languages Common, Draconic, Elvish, two ancient scripts
Challenge 2 (450 XP)
Catalogued Memory. The archivist has advantage on Intelligence checks made to recall lore about books, maps, lineages, ruins, and written magic.
Quiet Authority. The archivist has advantage on Charisma checks made to calm a creature inside a library, archive, or school.
Actions
Commanding Whisper. One creature within 30 feet that can hear the archivist must succeed on a DC 13 Wisdom saving throw or have disadvantage on the next attack roll it makes before the end of its next turn.
Reactions
Citation. When a creature the archivist can see makes an Intelligence, Wisdom, or Charisma check, the archivist adds 1d4 to the result.
Two-column Boss Output
Longer monsters can split into two columns on desktop while collapsing cleanly on smaller screens.
Thorn-Crowned Revenant
Medium undead, neutral evil
Armor Class 17 (thorn mail)
Hit Points 136 (16d8 + 64)
Speed 30 ft.
Saving Throws Con +8, Wis +7, Cha +8
Skills Intimidation +8, Perception +7
Damage Resistances necrotic; bludgeoning, piercing, and slashing from nonmagical attacks
Condition Immunities charmed, exhaustion, frightened, poisoned
Senses darkvision 120 ft., passive Perception 17
Languages Common, Sylvan
Challenge 10 (5,900 XP)
Legendary Resistance (3/Day). If the revenant fails a saving throw, it can choose to succeed instead.
Rooted Malice. The ground within 10 feet of the revenant is difficult terrain for its enemies.
Actions
Multiattack. The revenant makes two Thornblade attacks.
Thornblade. Melee Weapon Attack: +8 to hit, reach 5 ft., one target. Hit: 13 (2d8 + 4) slashing damage plus 10 (3d6) necrotic damage.
Briar Command (Recharge 5-6). Each creature of the revenant's choice within 20 feet must make a DC 16 Strength saving throw. On a failure, a creature takes 18 (4d8) piercing damage and is restrained until the end of its next turn.
Reactions
Grasping Root. When a creature the revenant can see moves out of its reach, the creature must succeed on a DC 16 Dexterity saving throw or its speed becomes 0 until the end of the turn.
Legendary Actions
The revenant can take 3 legendary actions, choosing from the options below. Only one legendary action option can be used at a time and only at the end of another creature's turn.
Move. The revenant moves up to half its speed without provoking opportunity attacks.
Thorn Lash. The revenant makes one Thornblade attack.
Bloom of Rot (Costs 2 Actions). Each enemy within 10 feet must succeed on a DC 16 Constitution saving throw or take 14 (4d6) poison damage.
Lair Actions
Black Vines. Vines erupt in a 20-foot square the revenant can see within 120 feet. The area becomes difficult terrain until initiative count 20 on the next round.
Wrappers
The renderer scans the page for these wrapper classes:
dnd-monster-data
Tetra-cube .monster JSON. This is the best class when pasting a saved file from the Tetra-cube statblock generator.
dnd-statblock-data
Hand-authored normalized JSON using simple fields such as name, armorClass, abilities, and sections.
Plain div wrapper
<div class="dnd-monster-data">
{
"...": "paste the full .monster JSON here"
}
</div>
template wrapper
<template class="dnd-monster-data">
{
"...": "paste the full .monster JSON here"
}
</template>
Display Options
Add these attributes to the wrapper. They apply to every monster inside that wrapper.
data-layout
one-column, two-column, wide, full
Overrides the width or column behavior. Use one-column for narrow pages and two-column for large monsters.
data-compact
true
Slightly tightens the block for long monsters.
data-copy-buttons
true
Adds small copy buttons beside detected attack bonuses, save DCs, and dice formulas.
data-copy
true
Short alias for data-copy-buttons="true".
data-show-source
true
Adds a collapsed source/debug disclosure below the rendered block.
data-source
true
Short alias for data-show-source="true".
data-source-label
Any text
Changes the label on the source/debug disclosure.
Option Example
<template
class="dnd-monster-data"
data-layout="one-column"
data-compact="true"
data-copy-buttons="true"
data-show-source="true"
data-source-label="Original .monster JSON">
{
"...": "paste the full .monster JSON here"
}
</template>
Copy/Paste Recipes
Use these when you know the behavior you want but do not want to rebuild the wrapper by hand. Replace the placeholder JSON with either the full contents of a .monster file or a hand-authored statblock object.
Most Compatible .monster Paste
This works well if BookStack is picky about allowed HTML. It uses a normal div, no optional controls, and no source disclosure.
<div class="dnd-monster-data">
{
"...": "paste the full .monster JSON here"
}
</div>
Clean Editing Wrapper
This is the preferred wrapper when BookStack keeps template tags. The raw JSON stays hidden before the renderer runs.
<template class="dnd-monster-data">
{
"...": "paste the full .monster JSON here"
}
</template>
Boss Monster with Copy Buttons
Use this for monsters where you will be copying attack bonuses, save DCs, or dice formulas during play.
<template
class="dnd-monster-data"
data-layout="two-column"
data-copy-buttons="true">
{
"...": "paste the full .monster JSON here"
}
</template>
Debug While Building
This keeps the original JSON attached below the statblock in a collapsed disclosure. It is useful while testing a new monster or diagnosing token output.
<template
class="dnd-monster-data"
data-copy-buttons="true"
data-show-source="true"
data-source-label="Original .monster JSON">
{
"...": "paste the full .monster JSON here"
}
</template>
Narrow Page or Sidebar-Friendly Monster
Use one column if a BookStack page has a narrow content area or the monster has long actions that read poorly in columns.
<template
class="dnd-monster-data"
data-layout="one-column"
data-compact="true">
{
"...": "paste the full .monster JSON here"
}
</template>
Hand-authored JSON Starter
Use this when you are writing a creature directly in BookStack instead of exporting from Tetra-cube.
<div class="dnd-statblock-data">
{
"name": "Creature Name",
"meta": "Medium humanoid, neutral",
"armorClass": "13",
"hitPoints": "27 (5d8 + 5)",
"speed": "30 ft.",
"abilities": {
"STR": "12 (+1)",
"DEX": "14 (+2)",
"CON": "12 (+1)",
"INT": "10 (+0)",
"WIS": "11 (+0)",
"CHA": "13 (+1)"
},
"properties": [
{ "label": "Skills", "value": "Perception +2" },
{ "label": "Senses", "value": "passive Perception 12" },
{ "label": "Languages", "value": "Common" },
{ "label": "Challenge", "value": "1 (200 XP)" }
],
"sections": [
{
"title": "Actions",
"features": [
{
"name": "Shortsword",
"text": "Melee Weapon Attack: +4 to hit, reach 5 ft., one target. Hit: 5 (1d6 + 2) piercing damage."
}
]
}
]
}
</div>
Tetra-cube .monster Files
The renderer understands Tetra-cube saved statblocks. A .monster file is JSON, so you can open it in a text editor and paste the entire contents into a wrapper. This is the easiest workflow when you already use Tetra-cube to create or edit monsters.
The main advantage of this path is that you do not have to translate the monster into a new schema. The renderer reads the Tetra-cube fields, computes the familiar derived values, resolves supported bracket tokens, and maps the saved sections into BookStack-friendly HTML.
.monster file.
Open the file as text.
Copy everything in the file.
Paste it into <div class="dnd-monster-data"> or <template class="dnd-monster-data">.
.monster file by URL. Paste the JSON inline. That avoids BookStack attachment permissions, CORS, and login/session issues.What the Renderer Reads from Tetra-cube
The converter looks for the normal Tetra-cube fields such as size, type, alignment, armor, hit points, speed, ability scores, saves, skills, damage types, senses, languages, CR, traits, actions, bonus actions, reactions, legendary actions, mythic actions, lair actions, and regional effects.
If the Tetra-cube file has doubleColumns enabled, the renderer will default the monster to a wider two-column layout. You can override that with data-layout="one-column" or force a two-column layout with data-layout="two-column".
What Not to Paste
.monster JSON instead.
Do not paste the file path to a .monster file. BookStack viewers will not be able to read a local path from your computer.
Do not wrap the data in a <script> tag. BookStack may strip it, and this renderer intentionally uses safer page-content wrappers.
Do not edit the rendered output after saving. Edit the JSON inside the wrapper, then let the renderer rebuild the display.
Using the Paste Helper
bookstack-monster-paste-helper.html locally.
Drop a .monster file, choose it with the file picker, or paste the JSON into the raw JSON box.
Select wrapper and display options.
Click Use Pasted JSON if you pasted manually.
Copy the generated HTML and paste it into BookStack source mode.
Hand-authored JSON Model
Use dnd-statblock-data when you want a compact JSON object instead of a Tetra-cube export. This is good for one-off NPCs, quick encounter variants, non-standard monsters, hazard-like creatures, or pages where you want to keep the data short and readable.
The hand-authored model is already close to the rendered output. You provide the text you want to see: armor class, hit points, speed, abilities, property rows, features, and sections. The renderer handles styling, layout, mobile behavior, dark mode, and optional copy buttons.
dnd-monster-data for exported Tetra-cube files. Use dnd-statblock-data when writing your own simplified JSON by hand.name
String
Monster name shown as the statblock title.
meta
String
Usually size, type, and alignment.
armorClass, hitPoints, speed
Strings
Core properties shown above abilities.
abilities
Object
Use STR, DEX, CON, INT, WIS, CHA. Values can include score and modifier.
properties
Array
Each item can be {"label":"Skills","value":"Perception +4"} or ["Skills","Perception +4"].
features or traits
Array
Passive traits before action sections. Items can be strings or {"name":"Trait","text":"Description"}.
sections
Array
Each section has title and features. Common titles are Actions, Bonus Actions, Reactions, Legendary Actions, Lair Actions, and Regional Effects.
columns
Array
Advanced two-column control. Usually easier to use data-layout="two-column".
Minimum Useful Hand-authored Block
A hand-authored monster can be very small. Only include the fields that matter for that creature. Empty or missing fields are skipped.
<div class="dnd-statblock-data">
{
"name": "Glass Mote",
"meta": "Tiny construct, unaligned",
"armorClass": "14",
"hitPoints": "5 (2d4)",
"speed": "0 ft., fly 30 ft. (hover)",
"abilities": {
"STR": "3 (-4)",
"DEX": "18 (+4)",
"CON": "10 (+0)",
"INT": "3 (-4)",
"WIS": "12 (+1)",
"CHA": "5 (-3)"
},
"properties": [
{ "label": "Senses", "value": "blindsight 30 ft., passive Perception 11" },
{ "label": "Challenge", "value": "1/4 (50 XP)" }
],
"sections": [
{
"title": "Actions",
"features": [
{ "name": "Shard Sting", "text": "Melee Weapon Attack: +6 to hit, reach 5 ft., one target. Hit: 6 (1d4 + 4) piercing damage." }
]
}
]
}
</div>
Examples
These examples are intentionally copy/pasteable. The .monster examples show wrapper patterns, because the actual exported file can be long. The hand-authored examples show full JSON objects so you can see the structure of properties, traits, sections, and actions.
Hand-authored Monster
This is a normal combat creature with properties and an Actions section. It also enables copy buttons so attack bonuses and dice formulas can be copied during play.
<div class="dnd-statblock-data" data-copy-buttons="true">
{
"name": "Lantern-Haunt Wight",
"meta": "Medium undead, lawful evil",
"armorClass": "15 (ancient mail)",
"hitPoints": "67 (9d8 + 27)",
"speed": "30 ft.",
"abilities": {
"STR": "16 (+3)",
"DEX": "12 (+1)",
"CON": "16 (+3)",
"INT": "11 (+0)",
"WIS": "14 (+2)",
"CHA": "15 (+2)"
},
"properties": [
{ "label": "Skills", "value": "Perception +4, Stealth +3" },
{ "label": "Damage Resistances", "value": "necrotic; bludgeoning, piercing, and slashing from nonmagical attacks" },
{ "label": "Senses", "value": "darkvision 60 ft., passive Perception 14" },
{ "label": "Languages", "value": "Common plus one language it knew in life" },
{ "label": "Challenge", "value": "4 (1,100 XP)" }
],
"sections": [
{
"title": "Actions",
"features": [
{
"name": "Graveblade",
"text": "Melee Weapon Attack: +5 to hit, reach 5 ft., one target. Hit: 8 (1d10 + 3) slashing damage plus 7 (2d6) necrotic damage."
}
]
}
]
}
</div>
Social NPC with Utility Actions
Not every statblock needs to be a full combat monster. This example keeps the combat surface small and emphasizes skills, languages, traits, and a reaction.
<div class="dnd-statblock-data">
{
"name": "Archivist of the Lower Stacks",
"meta": "Medium humanoid, lawful neutral",
"armorClass": "12",
"hitPoints": "33 (6d8 + 6)",
"speed": "30 ft.",
"abilities": {
"STR": "9 (-1)",
"DEX": "14 (+2)",
"CON": "12 (+1)",
"INT": "17 (+3)",
"WIS": "15 (+2)",
"CHA": "13 (+1)"
},
"properties": [
{ "label": "Saving Throws", "value": "Int +5, Wis +4" },
{ "label": "Skills", "value": "Arcana +5, History +7, Insight +4, Investigation +5" },
{ "label": "Senses", "value": "passive Perception 12" },
{ "label": "Languages", "value": "Common, Draconic, Elvish, two ancient scripts" },
{ "label": "Challenge", "value": "2 (450 XP)" }
],
"features": [
{
"name": "Catalogued Memory",
"text": "The archivist has advantage on Intelligence checks made to recall lore about books, maps, lineages, ruins, and written magic."
},
{
"name": "Quiet Authority",
"text": "The archivist has advantage on Charisma checks made to calm a creature inside a library, archive, or school."
}
],
"sections": [
{
"title": "Actions",
"features": [
{
"name": "Inkknife",
"text": "Melee or Ranged Weapon Attack: +4 to hit, reach 5 ft. or range 20/60 ft., one target. Hit: 4 (1d4 + 2) piercing damage."
},
{
"name": "Commanding Whisper",
"text": "One creature within 30 feet that can hear the archivist must succeed on a DC 13 Wisdom saving throw or have disadvantage on the next attack roll it makes before the end of its next turn."
}
]
},
{
"title": "Reactions",
"features": [
{
"name": "Citation",
"text": "When a creature the archivist can see makes an Intelligence, Wisdom, or Charisma check, the archivist adds 1d4 to the result."
}
]
}
]
}
</div>
Boss with Legendary Actions
This example shows a larger hand-authored block with traits, actions, reactions, legendary actions, and lair actions. Use data-layout="two-column" when the block is long enough that a single column feels too tall.
<div class="dnd-statblock-data" data-layout="two-column" data-copy-buttons="true">
{
"name": "Thorn-Crowned Revenant",
"meta": "Medium undead, neutral evil",
"armorClass": "17 (thorn mail)",
"hitPoints": "136 (16d8 + 64)",
"speed": "30 ft.",
"abilities": {
"STR": "18 (+4)",
"DEX": "14 (+2)",
"CON": "18 (+4)",
"INT": "12 (+1)",
"WIS": "16 (+3)",
"CHA": "18 (+4)"
},
"properties": [
{ "label": "Saving Throws", "value": "Con +8, Wis +7, Cha +8" },
{ "label": "Skills", "value": "Intimidation +8, Perception +7" },
{ "label": "Damage Resistances", "value": "necrotic; bludgeoning, piercing, and slashing from nonmagical attacks" },
{ "label": "Condition Immunities", "value": "charmed, exhaustion, frightened, poisoned" },
{ "label": "Senses", "value": "darkvision 120 ft., passive Perception 17" },
{ "label": "Languages", "value": "Common, Sylvan" },
{ "label": "Challenge", "value": "10 (5,900 XP)" }
],
"features": [
{
"name": "Legendary Resistance (3/Day)",
"text": "If the revenant fails a saving throw, it can choose to succeed instead."
},
{
"name": "Rooted Malice",
"text": "The ground within 10 feet of the revenant is difficult terrain for its enemies."
}
],
"sections": [
{
"title": "Actions",
"features": [
{
"name": "Multiattack",
"text": "The revenant makes two Thornblade attacks."
},
{
"name": "Thornblade",
"text": "Melee Weapon Attack: +8 to hit, reach 5 ft., one target. Hit: 13 (2d8 + 4) slashing damage plus 10 (3d6) necrotic damage."
},
{
"name": "Briar Command (Recharge 5-6)",
"text": "Each creature of the revenant's choice within 20 feet must make a DC 16 Strength saving throw. On a failure, a creature takes 18 (4d8) piercing damage and is restrained until the end of its next turn."
}
]
},
{
"title": "Reactions",
"features": [
{
"name": "Grasping Root",
"text": "When a creature the revenant can see moves out of its reach, the creature must succeed on a DC 16 Dexterity saving throw or its speed becomes 0 until the end of the turn."
}
]
},
{
"title": "Legendary Actions",
"features": [
"The revenant can take 3 legendary actions, choosing from the options below. Only one legendary action option can be used at a time and only at the end of another creature's turn.",
{
"name": "Move",
"text": "The revenant moves up to half its speed without provoking opportunity attacks."
},
{
"name": "Thorn Lash",
"text": "The revenant makes one Thornblade attack."
},
{
"name": "Bloom of Rot (Costs 2 Actions)",
"text": "Each enemy within 10 feet must succeed on a DC 16 Constitution saving throw or take 14 (4d6) poison damage."
}
]
},
{
"title": "Lair Actions",
"features": [
"On initiative count 20, losing initiative ties, the revenant can cause one of the following effects:",
{
"name": "Black Vines",
"text": "Vines erupt in a 20-foot square the revenant can see within 120 feet. The area becomes difficult terrain until initiative count 20 on the next round."
},
{
"name": "Whispering Graves",
"text": "One creature the revenant can see must succeed on a DC 16 Wisdom saving throw or be frightened until the end of its next turn."
}
]
}
]
}
</div>
Multiple Monsters in a Grid
Set "grid": true when a single wrapper contains several small statblocks. This is useful for encounter pages with minions, variants, or summoned creatures.
<div class="dnd-statblock-data">
{
"grid": true,
"monsters": [
{
"name": "Ash Rat",
"meta": "Tiny beast, unaligned",
"armorClass": "12",
"hitPoints": "7 (2d4 + 2)",
"speed": "30 ft.",
"abilities": {
"STR": "4 (-3)",
"DEX": "14 (+2)",
"CON": "12 (+1)",
"INT": "2 (-4)",
"WIS": "10 (+0)",
"CHA": "4 (-3)"
},
"properties": [
{ "label": "Senses", "value": "darkvision 30 ft., passive Perception 10" },
{ "label": "Challenge", "value": "1/8 (25 XP)" }
],
"sections": [
{
"title": "Actions",
"features": [
{ "name": "Bite", "text": "Melee Weapon Attack: +4 to hit, reach 5 ft., one target. Hit: 4 (1d4 + 2) piercing damage." }
]
}
]
},
{
"name": "Cinder Imp",
"meta": "Tiny fiend, chaotic evil",
"armorClass": "13",
"hitPoints": "10 (3d4 + 3)",
"speed": "20 ft., fly 30 ft.",
"abilities": {
"STR": "6 (-2)",
"DEX": "16 (+3)",
"CON": "12 (+1)",
"INT": "10 (+0)",
"WIS": "11 (+0)",
"CHA": "13 (+1)"
},
"properties": [
{ "label": "Damage Immunities", "value": "fire" },
{ "label": "Senses", "value": "darkvision 60 ft., passive Perception 10" },
{ "label": "Challenge", "value": "1/2 (100 XP)" }
],
"sections": [
{
"title": "Actions",
"features": [
{ "name": "Spark Claw", "text": "Melee Weapon Attack: +5 to hit, reach 5 ft., one target. Hit: 5 (1d4 + 3) slashing damage plus 3 (1d6) fire damage." }
]
}
]
}
]
}
</div>
Manual Columns for Hand-authored JSON
Most of the time, data-layout="two-column" is enough. If you want exact control over what goes in each column, use columns. Put "includeCore": true on the column that should contain armor class, hit points, speed, abilities, and property rows.
<div class="dnd-statblock-data" data-layout="wide">
{
"name": "Mirror-Salt Sentinel",
"meta": "Large construct, unaligned",
"armorClass": "18 (natural armor)",
"hitPoints": "95 (10d10 + 40)",
"speed": "30 ft.",
"abilities": {
"STR": "20 (+5)",
"DEX": "10 (+0)",
"CON": "18 (+4)",
"INT": "6 (-2)",
"WIS": "12 (+1)",
"CHA": "8 (-1)"
},
"properties": [
{ "label": "Damage Immunities", "value": "poison, psychic" },
{ "label": "Condition Immunities", "value": "charmed, exhaustion, frightened, paralyzed, petrified, poisoned" },
{ "label": "Senses", "value": "darkvision 60 ft., passive Perception 11" },
{ "label": "Languages", "value": "understands the languages of its creator but can't speak" },
{ "label": "Challenge", "value": "6 (2,300 XP)" }
],
"columns": [
{
"includeCore": true,
"features": [
{
"name": "Reflective Body",
"text": "When the sentinel is targeted by a spell attack, roll a d6. On a 6, the attack misses and the attacker becomes the target instead."
}
],
"sections": [
{
"title": "Actions",
"features": [
{
"name": "Multiattack",
"text": "The sentinel makes two Slam attacks."
},
{
"name": "Slam",
"text": "Melee Weapon Attack: +8 to hit, reach 10 ft., one target. Hit: 16 (2d10 + 5) bludgeoning damage."
}
]
}
]
},
{
"sections": [
{
"title": "Reactions",
"features": [
{
"name": "Saltglass Rebuke",
"text": "When a creature within 30 feet hits the sentinel with an attack, the sentinel forces that creature to make a DC 15 Constitution saving throw. On a failure, the creature takes 10 (3d6) radiant damage."
}
]
}
]
}
]
}
</div>
Full BookStack Page Snippet
The wrapper can sit between normal BookStack page content. Readers will see the prose and the rendered statblock, not the raw JSON.
<p>The sentinel waits in the ruined west transept. It attacks only if a creature crosses the salt line or touches the sealed door.</p>
<template class="dnd-statblock-data" data-copy-buttons="true">
{
"name": "Salt-Line Guardian",
"meta": "Medium construct, unaligned",
"armorClass": "16",
"hitPoints": "45 (6d8 + 18)",
"speed": "30 ft.",
"abilities": {
"STR": "16 (+3)",
"DEX": "12 (+1)",
"CON": "16 (+3)",
"INT": "5 (-3)",
"WIS": "12 (+1)",
"CHA": "6 (-2)"
},
"properties": [
{ "label": "Damage Resistances", "value": "radiant" },
{ "label": "Senses", "value": "darkvision 60 ft., passive Perception 11" },
{ "label": "Challenge", "value": "3 (700 XP)" }
],
"sections": [
{
"title": "Actions",
"features": [
{
"name": "Halberd",
"text": "Melee Weapon Attack: +5 to hit, reach 10 ft., one target. Hit: 8 (1d10 + 3) slashing damage."
}
]
}
]
}
</template>
<p>If the party speaks the old oath aloud, the guardian lowers its weapon and points toward the reliquary stair.</p>
Two-column Tetra-cube Export
This is the main pattern for a large monster exported from Tetra-cube. Replace the placeholder object with the full contents of the saved .monster file.
<template
class="dnd-monster-data"
data-layout="two-column"
data-copy-buttons="true">
{
"...": "paste the full Tetra-cube .monster JSON here"
}
</template>
Debuggable One-column Tetra-cube Export
This is useful while you are testing a new Tetra-cube monster. Once the monster looks right, you can remove data-show-source if you do not want the source disclosure on the page.
<template
class="dnd-monster-data"
data-layout="one-column"
data-compact="true"
data-show-source="true"
data-source-label="Original Tetra-cube JSON">
{
"...": "paste the full Tetra-cube .monster JSON here"
}
</template>
Tetra-cube-shaped JSON Excerpt
A real .monster export usually has more fields than this, but this excerpt shows the style of data the dnd-monster-data converter understands. Tetra-cube names fields differently from the hand-authored model, such as strPoints, hpText, damagetypes, and legendaries.
<div class="dnd-monster-data" data-copy-buttons="true">
{
"name": "Emberglass Drake",
"size": "medium",
"type": "dragon",
"alignment": "chaotic neutral",
"armorClass": 15,
"hpText": "76 (9d8 + 36)",
"speed": 30,
"flySpeed": 60,
"strPoints": 18,
"dexPoints": 14,
"conPoints": 18,
"intPoints": 8,
"wisPoints": 12,
"chaPoints": 15,
"cr": "5",
"skills": [
{ "name": "Perception", "note": "ex" },
{ "name": "Stealth" }
],
"damagetypes": [
{ "name": "fire", "type": "i" }
],
"darkvision": 60,
"languages": [
{ "name": "Draconic", "speaks": true }
],
"abilities": [
{
"name": "Heated Scales",
"desc": "A creature that touches the drake or hits it with a melee attack while within 5 feet takes 3 (1d6) fire damage."
}
],
"actions": [
{
"name": "Bite",
"desc": "_Melee Weapon Attack:_ [STR ATK] to hit, reach 5 ft., one target. _Hit:_ [STR 1D10] piercing damage plus [2D6] fire damage."
},
{
"name": "Glassfire Breath (Recharge 5-6)",
"desc": "The drake exhales fire in a 30-foot cone. Each creature in that area must make a [CON SAVE] Dexterity saving throw, taking [8D6] fire damage on a failed save, or half as much damage on a successful one."
}
]
}
</div>
Tetra-cube Token Support
Tetra-cube descriptions often contain bracket tokens. The renderer resolves the following patterns when converting .monster data.
[MON]
the dragon
Short monster name.
[MONS]
dragons
Plural monster name.
[STR], [DEX + 2]
+10, +4
Ability modifier, optionally adjusted.
[STR ATK], [DEX ATK - 1]
+17
Ability modifier plus proficiency, optionally adjusted.
[WIS SAVE], [CHA SAVE + 2]
DC 18
Save DC using 8 + ability modifier + proficiency, optionally adjusted.
[STR 2D6], [DEX 1D8 + 2]
17 (2d6 + 10)
Average dice damage plus an ability modifier.
[3D6], [2D10 + 4]
10 (3d6)
Plain dice average and display formula.
Token Example in a Tetra-cube Description
{
"name": "Bite",
"desc": "_Melee Weapon Attack:_ [STR ATK] to hit, reach 10 ft., one target. _Hit:_ [STR 2D10] piercing damage."
}
Formatting Notes
**bold** text is rendered as bold.
_italic_ text is rendered as italic.
Lines beginning with > render as hanging-indent lines, useful for spell-style lists or nested effects.
Melee Weapon Attack:, Ranged Weapon Attack:, and Hit: are styled like 5e labels.
Print mode hides copy controls and source/debug disclosures.
Dark mode is supported through BookStack's .dark-mode class and matching color variables.
Troubleshooting
The JSON text appears on the page instead of a statblock.
The CSS or script is probably not installed in BookStack custom head content, or the wrapper class is wrong. Check for dnd-monster-data or dnd-statblock-data.
BookStack will not save the wrapper.
Try source/HTML mode. If template is stripped, switch to the plain div wrapper. Keep the JSON inline and do not use <script type="application/json">.
An error box says "Unexpected end of JSON input."
The pasted JSON is incomplete, usually missing a closing brace or bracket. The error box includes a line, column, and excerpt near the problem.
The block is too wide or too cramped.
Use data-layout="one-column" for narrow pages, data-layout="two-column" for long monsters, data-layout="wide" for large desktop pages, or data-compact="true" for dense entries.
Copy buttons are visible but not needed.
Remove data-copy-buttons="true". Copy controls are opt-in, so plain pasted .monster wrappers will not show them unless requested.
I want to inspect the original data after render.
Add data-show-source="true". The original JSON will be hidden in a collapsed disclosure after the statblock.
This is a test.