Skip to content

Widgets

Widgets are reusable content blocks defined in your story and invoked by name.

Defining a Widget

Use the {widget} macro to define a widget. The first argument is the widget's name (quoted string):

:: StoryInit
{widget "StatusBar"}
  Health: {$health} | Mana: {$mana}
{/widget}

{widget "Separator"}
  <hr>
{/widget}

Widgets can be defined in two places:

  • StoryInit — the most common location. Widgets defined here are available everywhere.
  • Any passage tagged widget — these passages are automatically processed at startup, so their widgets are also available from the start.
:: MyWidgets [widget]
{widget "StatusBar"}
  Health: {$health} | Mana: {$mana}
{/widget}

{widget "Separator"}
  <hr>
{/widget}

Using [widget]-tagged passages lets you organize widget definitions into dedicated passages and keep StoryInit focused on variable setup and initialization logic.

Using a Widget

Invoke a widget by using its name as a macro:

{StatusBar}

Some passage content...

{Separator}

More content.

Widget names are case-insensitive: {statusbar}, {StatusBar}, and {STATUSBAR} all work.

Arguments

Widgets can accept arguments, making them more flexible. Declare parameter names after the widget name using @ prefixed local variables:

:: StoryInit
{widget "StatLine" @label @value @max}
  **{@label}:** {@value} / {@max}
{/widget}

Pass arguments as comma-separated expressions when invoking the widget:

:: Start
{StatLine "Health", $health, $max_health}
{StatLine "Mana", $mana, $max_mana}
{StatLine "XP", $xp, $xp_needed}

Arguments are evaluated as expressions, so you can pass variables, literals, or computed values:

{StatLine "Damage", $strength * 2, 100}

Parameters are block-scoped to the widget body using the @ namespace — they never conflict with $ story variables or _ temporary variables. If fewer arguments are passed than parameters declared, the extra parameters are undefined.

How Widgets Work

When you define a widget, its body is stored as an AST (parsed content). When you invoke it, that AST is rendered in place. This means:

  • Widgets see the current values of all variables at the time they are rendered
  • Widgets re-render when variables they reference change
  • Widgets can contain any markup: links, macros, HTML, other widgets
  • Arguments are evaluated at invocation time and scoped to the widget body

Example

A simple widget without arguments:

:: StoryInit
{widget "HealthBar"}
  {if $health > 50}
    {.green print $health}
  {elseif $health > 0}
    {.yellow print $health}
  {else}
    {.red print "DEAD"}
  {/if}
{/widget}

:: Start
HP: {HealthBar}

You stand at the entrance to the dungeon.
[[Enter the dungeon|Dungeon]]

:: Dungeon
HP: {HealthBar}

{set $health = $health - 20}
A trap! You take damage.

A parameterized widget for reusable UI:

:: StoryInit
{widget "ResourceBar" @label @current @maximum}
  <div class="resource-bar">
    <span class="resource-label">{@label}</span>
    {meter @current @maximum}
  </div>
{/widget}

:: Start
{ResourceBar "HP", $health, $max_health}
{ResourceBar "MP", $mana, $max_mana}
{ResourceBar "Stamina", $stamina, $max_stamina}

Released under the Unlicense.