Skip to main content

Stat Test Page

# D20 Hiraeth Archives & Guides

BookStack D&D 5e Statblock Guide

This guide documents how to use the BookStack D&D 5e statblock renderer with inline JSON and Tetra-cube `.monstermonster` workflowfiles.

for the BookStack statblock renderer.

Install the renderer once in BookStack custom HTML head content,content. thenAfter pastethat, smalleach HTMLpage wrapperscan intoinclude anyone page.or Themore wrapperhidden containsJSON JSON;wrappers. When the page loads, the renderer turnsreplaces thatthose JSONwrappers into awith styled D&D 5e monster statblock when the page loads.statblocks.

Files

Main Renderer

- `bookstack-custom-html-with-json-statblocks.html

html`  

Contains
 the CSSThe and JavaScript that should live inmain BookStack custom HTMLHTML/CSS/JS head content.bundle.

-

Paste Helper

`bookstack-monster-paste-helper.html

html`  

Local
  A local helper page for loading, dropping, or pasting a `.monstermonster` file and generating theBookStack-ready wrapper HTML.

-

This Guide

`bookstack-dnd-statblock-guide.htmlhtml`  
  The full HTML guide with rendered previews.

Human-readable- usage`bookstack-dnd-statblock-guide.md`  reference
 with examplesThis youMarkdown can copy into BookStack source mode.version.

##

Quick Start

This1. 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.

    Open `bookstack-custom-html-with-json-statblocks.htmlhtml`.
    2. Copy the statblock CSS and scriptJavaScript into BookStack's **Custom HTML Head ContentContent**.
    3. area. Open a BookStack page in source or source/HTML mode.
    4. Paste one of the wrapper snippets below.
    5. Put the JSON from a wrapper`.monster` such as <div class="dnd-monster-data">...</div> with JSONfile inside it.the Usewrapper.
    6. dnd-monster-data for Tetra-cube .monster files and dnd-statblock-data for hand-authored JSON. Save the page.

    The renderer finds the wrapper, parses the JSON, hides the wrapperraw data, and replaces it with a styled D&Dstatblock.

    5e

    ## statblock.

    Correct `.monster`
    Recommended:Wrapper

    Use this first. It is the cleanest option because `<template class="dnd-monster-data"> when BookStack allows it. Template` content is naturallystays 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

    Thing Where It Goes How Often Statblock CSS BookStack Custom HTML Head Content Once per BookStack instance or theme setup. Statblock JavaScript BookStack Custom HTML Head Content, after the CSS Once per BookStack instance or theme setup. .monster JSON Inside a page wrapper such as ```html
    <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.
    Editing rule: edit the monster by editing the JSON inside the wrapper. Do not edit the rendered statblock HTML directly, because the script regenerates that HTML from the JSON on page load.

    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.

    Wrappers

    The renderer scans the page for these wrapper classes:

    Wrapper Use For 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

    Most compatible
    <div class="dnd-monster-data">
    { "...": "paste the full .monster JSON here"
    
    }
    </divtemplate>

    ```

    template

    For wrappera

    Cleanerreal before`.monster` render
    file,
    paste the **entire file contents** between the opening and closing tags:
    

    ```html
    <template class="dnd-monster-data">
    {
      "name": "Ancient Red Dragon",
      "size": "gargantuan",
      "type": "dragon",
      "...": "the rest of the saved Tetra-cube .monster JSON"
    }
    </template>
    ```

    Important: when pasting into BookStack, paste only the HTML snippet. Do not paste the Markdown code fence lines.

    ## Most Compatible Wrapper

    If BookStack strips `<template>` tags, use a normal `<div>` instead.

    ```html
    <div class="dnd-monster-data">
    {
      "...": "paste the full .monster JSON here"
    }
    </templatediv>

    Display Options


    ```

    AddThis theseis attributesslightly toless clean before render, but the wrapper.CSS Theyhides applyit to every monster inside that wrapper.

    Attribute Values Effect data-layout one-column, two-column, wide, full Overridesand the widthrenderer orstill columnreplaces behavior.it Usewith one-columnthe statblock.

    This is the pattern I would use for narrowmost BookStack pages andwhile two-columnbuilding/testing formonsters:

    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

    ```html
    <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


    ```

    UseWhat thesethose whenattributes do:

    - `data-copy-buttons="true"` adds small copy controls beside attack bonuses, save DCs, and dice formulas.
    - `data-show-source="true"` adds a collapsed source/debug block with the original JSON.
    - `data-source-label="Original .monster JSON"` changes the label on that collapsed source block.

    After the monster is working, you knowcan theremove behavior`data-show-source="true"` if you want but do not want the source/debug disclosure visible.

    ## Layout Options

    Add these attributes 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.wrapper.

    Most

    ```html
    <template
     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"
      data-layout="one-column">
    {
     { "...": "paste the full .monster JSON here"
    }
    </divtemplate>

    Clean Editing Wrapper


    ```

    ThisAvailable islayout values:

    - `data-layout="one-column"`  
      Forces one-column output. Good for narrow BookStack pages or long monster actions.

    - `data-layout="two-column"`  
      Forces two-column output on desktop. Good for large monsters or boss statblocks.

    - `data-layout="wide"`  
      Makes the preferredblock wrapperwider.

    when

    - BookStack`data-layout="full"`  keeps
     template tags. The raw JSON stays hidden beforeLets the rendererblock runs.use the full available content width.

    Compact example:

    ```html
    <template
      class="dnd-monster-data"
      data-layout="one-column"
      data-compact="true">
    {
     { "...": "paste the full .monster JSON here"
    }
    </template>


    ```

    Boss Monstermonster with Copy Buttonsexample:

    Use this for monsters where you will be copying attack bonuses, save DCs, or dice formulas during play.

    ```html
    <template
      class="dnd-monster-data"
      data-layout="two-column"
      data-copy-buttons="true">
    {
     { "...": "paste the full .monster JSON here"
    }
    </template>

    Debug While Building


    ```

    This## keepsTetra-cube `.monster` Workflow

    1. Build or edit the originalmonster JSONat attachedTetra-cube.
    2. belowClick **Save Statblock**.
    3. Open the statblockdownloaded `.monster` file in a collapsedtext disclosure.editor.
    4. ItCopy isall usefulof whilethe testingJSON.
    5. Paste it inside a new`dnd-monster-data` monsterwrapper.
    6. orSave diagnosingthe tokenBookStack output.page.

    Do not paste:

    - The visible statblock text from Tetra-cube.
    - A local file path to the `.monster` file.
    - An attachment URL.
    - A `<script type="application/json">` block.

    The renderer expects inline JSON inside:

    ```html
    <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.or:

      In Tetra-cube, use Save Statblock to download the .monster file. Open the file as text. Copy everything in the file. Paste it into

      ```html
      <div class="dnd-monster-data">

       or <template class="dnd-monster-data">
      .
      The renderer does not fetch an attached .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

        Do not paste the visible statblock text from the Tetra-cube web page. Use the saved .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

          Open 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.

          Rule of thumb: use dnd-monster-data for exported Tetra-cube files. Use dnd-statblock-data when writing your own simplified JSON by hand.
          Field Shape Notes 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## examplesFull areExample: intentionally`.monster`-Style 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.Block

          Hand-authored Monster

          This is a normalshortened combatTetra-cube-style creatureexample. withA propertiesreal and`.monster` anfile Actionswill section.usually Ithave alsomore enables copy buttons so attack bonuses and dice formulas can be copied during play.fields.

          ```html
          <divtemplate
            class="dnd-statblock-monster-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."
              }
            ]
          }
          </divtemplate>

          Tetra-cube Token Support


          ```

          Tetra-cube## descriptionsHand-authored oftenJSON containWrapper

          bracket

          Use tokens. The renderer resolves the following patterns`dnd-statblock-data` when convertingyou .monsterare data.

          writing
          your own simplified TokenJSON Exampleinstead Output Meaning [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 DCof 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

          `.monster`
          {
            "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. file.

            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.

            For most monsters, use this pattern. It keeps the page clean, supports copy buttons, and preserves the original .monster data for debugging.

            <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>

            If BookStack removes template, use the same attributes on ```html
            <div class="dnd-monster-statblock-data">
            .


            {
             

            This is a test.

            { "name": "Lantern-Haunt Wight",
              "meta": "Medium undead, lawful evil",
             "theme": "undead", "layout": "wide", "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."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>
            ``` }

            ##

            Multiple Hand-authored Monsters

            Use `grid: true` and a `monsters` array.

            ```html
            <div class="dnd-statblock-data">
            {
              "grid": true,
              "monsters": [
                {
                  "name": "AncientAsh RedRat",
             Dragon"     "meta": "Tiny beast, unaligned",
                  "size"armorClass": "gargantuan"12",
                  "type"hitPoints": "Dragon","tag":"7 (2d4 + 2)",
                  "alignment"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",
                  "hitDice"armorClass": "28"13",
                  "armorName"hitPoints": "natural armor","shieldBonus":0,"natArmorBonus":12,"otherArmorDesc":"2210 (natural3d4 armor)+ 3)",
                  "speed": "40","burrowSpeed":"0","climbSpeed":"40","flySpeed":"80","hover":false,"swimSpeed":"0","customHP":false,"customSpeed":false,"hpText":"546 (28d20 + 252)","speedDesc":"40 ft., climb 4020 ft., fly 8030 ft.",
                  "strPoints"abilities": {
                    "30",STR": "dexPoints":"10","conPoints":"29","intPoints":"18","wisPoints":"15","chaPoints":"23","blindsight":"60","blind":false,"darkvision":"120","tremorsense":"0","truesight":"0","telepathy":0,"cr":"24","customCr":"246 (62,000-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)" }
                  ],
                  "customProf"sections":7, [
                    {
                      "isLegendary"title":true, "legendariesDescription"Actions",
                      "features": [
                        {
                          "Thename": dragon"Spark canClaw",
             take             "text": "Melee Weapon Attack: +5 to hit, reach 5 ft., one target. Hit: 5 (1d4 + 3) slashing damage plus 3 legendary(1d6) actions,fire choosingdamage."
             from           }
                      ]
                    }
                  ]
                }
              ]
            }
            </div>
            ```

            ## Supported Wrapper Attributes

            | Attribute | Values | Purpose |
            |---|---|---|
            | `data-layout` | `one-column`, `two-column`, `wide`, `full` | Changes statblock width/columns. |
            | `data-compact` | `true` | Slightly tightens long blocks. |
            | `data-copy-buttons` | `true` | Adds copy buttons for attacks, DCs, and dice. |
            | `data-copy` | `true` | Alias for `data-copy-buttons="true"`. |
            | `data-show-source` | `true` | Adds a collapsed original-source/debug block. |
            | `data-source` | `true` | Alias for `data-show-source="true"`. |
            | `data-source-label` | Any text | Changes the optionssource below.disclosure Onlylabel. one|

            legendary

            ## actionTetra-cube optionToken canSupport

            be

            The usedrenderer atresolves acommon timeTetra-cube bracket tokens:

            | Token | Meaning |
            |---|---|
            | `[MON]` | Short monster name. |
            | `[MONS]` | Plural monster name. |
            | `[STR]`, `[DEX + 2]` | Ability modifier, optionally adjusted. |
            | `[STR ATK]`, `[DEX ATK - 1]` | Ability modifier plus proficiency. |
            | `[WIS SAVE]`, `[CHA SAVE + 2]` | Save DC using 8 + ability modifier + proficiency. |
            | `[STR 2D6]`, `[DEX 1D8 + 2]` | Average dice damage plus ability modifier. |
            | `[3D6]`, `[2D10 + 4]` | Plain dice average and onlydisplay atformula. the|

            end

            Example:

            of

            ```json
            {
             another creature's turn. The dragon regains spent legendary actions at the start of its turn.","isLair":false,"lairDescription":"When fighting inside its lair, the ancient red dragon can invoke the ambient magic to take lair actions. On initiative count 20 (losing initiative ties), the ancient red dragon can take one lair action to cause one of the following effects:","lairDescriptionEnd":"The ancient red dragon can't repeat an effect until they have all been used, and it can't use the same effect two rounds in a row.","isMythic":false,"mythicDescription":"If the ancient red dragon's mythic trait is active, it can use the options below as legendary actions for 1 hour after using {Some Ability}.","isRegional":false,"regionalDescription":"The region containing the ancient red dragon's lair is warped by the creature's presence, which creates one or more of the following effects:","regionalDescriptionEnd":"If the ancient red dragon dies, the first two effects fade over the course of 3d10 days.","properties":[],"abilities":[{"name": "LegendaryBite",
             Resistance (3/Day)","desc":"If the dragon fails a saving throw, it can choose to succeed instead."}],"actions":[{"name":"Multiattack","desc":"The dragon can use its Frightful Presence. It then makes three attacks: one with its bite and two with its claws."},{"name":"Bite","desc":"_Melee Weapon Attack:_ +17[STR to hit, reach 15 ft., one target. _Hit:_ 21 (2d10 + 10) piercing damage plus 14 (4d6) fire damage."},{"name":"Claw","desc":"_Melee Weapon Attack:_ +17ATK] to hit, reach 10 ft., one target. _Hit:_ 17[STR (2d62D10] + 10) slashingpiercing damage."
            },{"name":"Tail","desc":"_Melee
            ```

            Weapon

            ## Attack:_Troubleshooting

            +17

            ### toThe hit,raw reachJSON 20appears ft.,on the page

            The renderer CSS/JS is probably not installed in BookStack custom head content, or the wrapper class is wrong.

            Check that you used one target.of _Hit:_these:

            19

            ```html
            <template (2d8class="dnd-monster-data">
            ...
            </template>
            ```

            +

            ```html
            <div 10)class="dnd-monster-data">
            ...
            </div>
            ```

            bludgeoning

            ```html
            <div damage.class="},{"name":"Frightfuldnd-statblock-data">
            ...
            </div>
            ```

            Presence","desc":"Each

            ### creatureBookStack ofwill not save the dragon'swrapper

            choice

            Use thatsource/HTML ismode. withinIf 120BookStack feetstrips of`<template>`, use the dragon`<div>` andwrapper.

            aware

            ### ofThe itpage mustshows succeed on a DC 21 Wisdom saving throw or become frightened for 1 minute. A creature can repeat the saving throw at the`Unexpected end of eachJSON ofinput`

            its

            The turns,pasted endingJSON is incomplete. Check for a missing closing `}` or `]`.

            ### The block is too wide

            Use:

            ```html
            data-layout="one-column"
            ```

            ### The block is too long

            Try:

            ```html
            data-layout="two-column"
            ```

            or:

            ```html
            data-compact="true"
            ```

            ## Best Default Snippet

            Use this for most Tetra-cube `.monster` files:

            ```html
            <template
              class="dnd-monster-data"
              data-copy-buttons="true"
              data-show-source="true"
              data-source-label="Original .monster JSON">
            {
              "...": "paste the effectfull on.monster itselfJSON onhere"
            }
            </template>
            ```

            a success.

            If aBookStack creature'sremoves saving`<template>`, throwuse isthis successfulinstead:

            or

            ```html
            <div
              class="dnd-monster-data"
              data-copy-buttons="true"
              data-show-source="true"
              data-source-label="Original .monster JSON">
            {
              "...": "paste the effectfull ends.monster forJSON it, the creature is immune to the dragon's Frightful Presence for the next 24 hours."here"
            },{"name":"Fire Breath (Recharge 5-6)","desc":"The dragon exhales fire in a 90-foot cone. Each creature in that area must make a DC 24 Dexterity saving throw, taking 91 (26d6) fire damage on a failed save, or half as much damage on a successful one."}],"bonusActions":[],"reactions":[],"legendaries":[{"name":"Detect","desc":"The dragon makes a Wisdom (Perception) check."},{"name":"Tail Attack","desc":"The dragon makes a tail attack."},{"name":"Wing Attack (Costs 2 Actions)","desc":"The dragon beats its wings. Each creature within 15 ft. of the dragon must succeed on a DC 25 Dexterity saving throw or take 17 (2d6 + 10) bludgeoning damage and be knocked prone. The dragon can then fly up to half its flying speed."}],"mythics":[],"lairs":[],"regionals":[],"sthrows":[{"name":"dex","order":1},{"name":"con","order":2},{"name":"wis","order":4},{"name":"cha","order":5}],"skills":[{"name":"perception","stat":"wis","note":" (ex)"},{"name":"stealth","stat":"dex"}],"damagetypes":[{"name":"fire","note":" (Immune)","type":"i"}],"specialdamage":[],"conditions":[],"languages":[{"name":"Common","speaks":true},{"name":"Draconic","speaks":true}],"understandsBut":"","shortName":"","pluralName":"","doubleColumns":true,"separationPoint":3,"damage":[]}


            </div>
            ```