Core tags and attributes

Much like HTML has its own native tags, Marko includes core tags and global attributes for declaratively building modern applications.

<if>, <else-if>, <else>

Like the equivalent JavaScript statements, these tags render conditional content:

  Hey there
  Bye now
  What’s up?
if(arriving) -- Hey there
else-if(leaving) -- Bye now
else -- What’s up?

They support any JavaScript expression in their tag arguments:

<if(Math.random() > 0.5)>
  <p>50% chance to see this</p>
if(Math.random() > 0.5)
    p -- 50% chance to see this


The <for> tag iterates over arrays/array-likes, object properties, and ranges of numbers.

Iterating over a list

Like the JavaScript for...of loop statement, giving <for>’s of attribute a value will loop over that value as an array or iterable.

The current item, index, and the iterating list are provided as tag parameters:

$ const colors = ["red", "green", "blue"];
  <for|color, index, colorList| of=colors>
    <li value=index>${color}</li>
$ const colors = ["red", "green", "blue"];
    for|color, index, colorList| of=colors
        li value=index -- ${color}

The output HTML would be:

  <li value="0">red</li>
  <li value="1">green</li>
  <li value="2">blue</li>

Pro Tip: <for>’s of attribute can loop over any iterable, just like JavaScript’s for...of. This includes strings, NodeLists, Sets… any object with zero-indexed numeric properties and a .length, basically.

Iterating over an object’s properties

Like JavaScript’s loop statement, giving <for> an object as its in attribute will loop over that object’s properties.

The current property name and property value are provided as tag parameters:

$ const settings = {
  "Dark Mode": false,
  "Fullscreen": true

  <for|name, enabled| in=settings>
    <dd>${enabled ? "on" : "off"}</dd>
$ const settings = {
    "Dark Mode": false,
    Fullscreen: true
    for|name, enabled| in=settings
        dt -- ${name}:
        dd -- ${enabled ? "on" : "off"}

The output HTML would be:

  <dt>Dark Mode:</dt>

Iterating between a range of numbers

The final <for> variant loops between two numbers, by providing from and to attributes. The current number in the range will be provided as a tag parameter:

<ol type="I">
  <for|i| from=0 to=10>
    <li value=i>${i}</li>
ol type="I"
    for|i| from=0 to=10
        li value=i -- ${i}

You can also pass an optional step attribute, which defaults to 1 otherwise. step lets you increment by a specific amount:

<ol type="I">
  <for|i| from=0 to=10 step=2>
    <li value=i>${i}</li>
ol type="I"
    for|i| from=0 to=10 step=2
        li value=i -- ${i}


<ol type="I">
  <li value="0">0</li>
  <li value="2">2</li>
  <li value="4">4</li>
  <li value="6">6</li>
  <li value="8">8</li>
  <li value="10">10</li>
ol type="I"
    li value="0" -- 0
    li value="2" -- 2
    li value="4" -- 4
    li value="6" -- 6
    li value="8" -- 8
    li value="10" -- 10

ProTip: This syntax is for generating numbers from nothing. Don’t use it to iterate over an object, like so:

<!-- Inefficient code, do not copy -->
  <for|i| from=0 to=(myArray.length - 1)>
// Inefficient code, do not copy
    for|i| from=0 to=(myArray.length - 1)
        li -- ${myArray[i]}

Use <for of> instead.


Warning: Using <while> is not recommended. Instead, replicate it with an iterable and <for>.

In the future, Marko may restrict value mutation during rendering, for runtime optimizations.

You can repeat a chunk of markup until a condition is met with the while tag:

$ let n = 0;

<while(n < 4)>
$ let n = 0;
while(n < 4)
    p -- ${n++}




Macros create reusable markup fragments for later use in the same template they were defined in.

The <macro> tag defines a macro as a tag via the name attribute. For example, the following macro is registered as the <greeting> tag:

<macro name="greeting">

macro name="greeting"
    p -- Welcome!

…the output HTML would be:


Macros become more useful with tag parameters, allowing complex templates. In this next example, <greeting> can now receive firstName and count parameters from its parent:

<macro|{ firstName, count }| name="greeting">
  <p>Hello ${firstName}!
    <output>You have ${count} new messages.</output>

<greeting firstName="Frank" count=20/>
macro|{ firstName, count }| name="greeting"
        Hello ${firstName}!
        <output>You have ${count} new messages.</output>
greeting firstName="Frank" count=20

…the output HTML would be:

  Hello Frank!
  <output>You have 20 new messages.</output>

Macros receive input like components do, including a renderBody for provided body content:

<macro|{ renderBody }| name="special-heading">

macro|{ renderBody }| name="special-heading"
        -- <${renderBody}/>!
special-heading -- Hello

…the output HTML would be:


ProTip: You can use a macro inside itself for recursive layouts, like displaying directory contents.


The <await> tag renders markup asynchronously using a Promise.

  • Its <@then> attribute tag displays when the Promise resolves, optionally receiving the resolved value as a tag parameter.
  • Its <@catch> attribute tag displays when the Promise rejects, optionally receiving the rejected value as a tag parameter.
  • Its optional <@placeholder> attribute tag displays while the Promise is pending.
$ const personPromise = new Promise((resolve, reject) => {
  setTimeout(() => resolve({ name: 'Frank' }), 1000);

<await(personPromise) client-reorder=true>
    <!-- Displays while promise is pending -->

    <!-- Displays if promise resolves -->
    <p>Hello ${}!</p>

    <!-- Displays if promise rejects -->
    ${} error: ${err.message}
$ const personPromise = new Promise((resolve, reject) => {
    setTimeout(() => resolve({ name: "Frank" }), 1000);
await(personPromise) client-reorder=true
        // Displays while promise is pending
            -- Loading…
        // Displays if promise resolves
        p -- Hello ${}!
        <!-- Displays if promise rejects --> ${} error: ${err.message}

Optional attributes for <await>:

timeoutintegerAn optional timeout. If reached, rejects the promise with a TimeoutError.
namestringImproves debugging and ensures ordering with the show-after attribute.
show-afterstringAnother <await> tag’s name. With client-reorder, ensures that the current <await> block will always show after the named <await>.
client-reorderbooleanIf true, anything after this <await> will be server-rendered before the Promise completes, then the fulfilled Promise’s result will be updated with client-side JavaScript.

Pro Tip: When using timeout, you can distinguish between TimeoutErrors and promise rejections by checking the error’s name:

<await(slowPromise) timeout=5000>
    <if( === "TimeoutError")>
      Took too long to fetch the data!
      Promise failed with ${err.message}.
await(slowPromise) timeout=5000
    @then -- Done
        if( === "TimeoutError") -- Took too long to fetch the data!
        else -- Promise failed with ${err.message}.


<include-text> inlines text files into a template, escaping HTML syntax characters (<, ", etc.).


If you do not want escaping, use <include-html> instead.


Like <include-text>, <include-html> inlines the contents of a file. However, this tag does not escape special HTML characters.



Marko removes HTML comment tags from its output. But if you need comments in the output, that’s what <html-comment> is for:

<html-comment>[if IE]><script src="html-shiv.js"></script><![endif]</html-comment>
html-comment -- [if IE]><script src="html-shiv.js"></script><![endif]


<!--[if IE]><script src="html-shiv.js"></script><![endif]-->


Helpful? You can thank these awesome people! You can also edit this doc if you see any issues or want to improve it.

Chat in Marko's Discord Server