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:
<if(arriving)> Hey there </if> <else-if(leaving)> Bye now </else-if> <else> What’s up? </else>
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>
if(Math.random() > 0.5) p -- 50% chance to see this
<for>
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"]; <ol> <for|color, index, colorList| of=colors> <li value=index>${color}</li> </for> </ol>
$ const colors = ["red", "green", "blue"]; ol for|color, index, colorList| of=colors li value=index -- ${color}
The output HTML would be:
<ol> <li value="0">red</li> <li value="1">green</li> <li value="2">blue</li> </ol>
Pro Tip:
<for>
’sof
attribute can loop over any iterable, just like JavaScript’sfor...of
. This includes strings,NodeList
s,Set
s… any object with zero-indexed numeric properties and a.length
, basically.
Iterating over an object’s properties
Like JavaScript’s for...in
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 }; <dl> <for|name, enabled| in=settings> <dt>${name}:</dt> <dd>${enabled ? "on" : "off"}</dd> </for> </dl>
$ { const settings = { "Dark Mode": false, Fullscreen: true, }; } dl for|name, enabled| in=settings dt -- ${name}: dd -- ${enabled ? "on" : "off"}
The output HTML would be:
<dl> <dt>Dark Mode:</dt> <dd>off</dd> <dt>Fullscreen:</dt> <dd>on</dd> </dl>
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> </for> </ol>
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> </for> </ol>
ol type="I" for|i| from=0 to=10 step=2 li value=i -- ${i}
…becomes:
<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>
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 --> <ul> <for|i| from=0 to=(myArray.length - 1)> <li>${myArray[i]}</li> </for> </ul><!-- Inefficient code, do not copy --> ul for|i| from=0 to=myArray.length - 1 li -- ${myArray[i]}Use
<for of>
instead.
<while>
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)> <p>${n++}</p> </while>
$ let n = 0; while(n < 4) p -- ${n++}
…becomes:
<p>0</p> <p>1</p> <p>2</p> <p>3</p>
<macro>
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"> <p>Welcome!</p> </macro> <greeting/> <greeting/>
macro name="greeting" p -- Welcome! greeting greeting
…the output HTML would be:
<p>Welcome!</p> <p>Welcome!</p>
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> </p> </macro> <greeting firstName="Frank" count=20/>
macro|{ firstName, count }| name="greeting" p -- Hello ${firstName}! output -- You have ${count} new messages. greeting firstName="Frank" count=20
…the output HTML would be:
<p> Hello Frank! <output>You have 20 new messages.</output> </p>
Macros receive input like components do, including a renderBody
for provided body content:
<macro|{ renderBody }| name="special-heading"> <h1> <${renderBody}/>! </h1> </macro> <special-heading> Hello </special-heading>
macro|{ renderBody }| name="special-heading" h1 ${renderBody} -- ! special-heading -- Hello
…the output HTML would be:
<h1>Hello!</h1>
ProTip: You can use a macro inside itself for recursive layouts, like displaying directory contents.
<await>
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> <@placeholder> <!-- Displays while promise is pending --> <label>Loading… <progress></progress> </label> </@placeholder> <@then|person|> <!-- Displays if promise resolves --> <p>Hello ${person.name}!</p> </@then> <@catch|err|> <!-- Displays if promise rejects --> ${err.name} error: ${err.message} </@catch> </await>
$ { const personPromise = new Promise((resolve, reject) => { setTimeout(() => resolve({ name: "Frank" }), 1000); }); } await(personPromise) client-reorder @placeholder <!-- Displays while promise is pending --> label -- Loading… progress @then|person| <!-- Displays if promise resolves --> p -- Hello ${person.name}! @catch|err| -- <!-- Displays if promise rejects -->${err.name} error: ${err.message}
Optional attributes for <await>
:
Attribute | Type | Description |
---|---|---|
timeout | integer | An optional timeout. If reached, rejects the promise with a TimeoutError . |
name | string | Improves debugging and ensures ordering with the show-after attribute. |
show-after | string | Another <await> tag’s name . Use with client-reorder to ensure that the current <await> will always render alongside or after the named <await> . |
client-reorder | boolean | If 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. |
Regardless of these attributes, the promise is executed as eagerly as possible. The attributes control how to coordinate rendering with the rest of the page:
client-reorder
prevents<await>
blocks from delaying the HTTP stream, at the expense of making their rendering rely on client-side JS. Useful for making non-critical page sections not block HTML streaming of important content.Using
show-after
withclient-reorder
ensures that the current<await>
block will always render simultaneously with or after the named<await>
. Useful for cutting down on layout shift.<@placeholder>
s can help fine-tune the user experience while loading.timeout
is useful for limiting non-critical content from slowing down the rest of the page too much.
Pro Tip: When using
timeout
, you can distinguish betweenTimeoutError
s and promise rejections by checking the error’sname
:<await(slowPromise) timeout=5000> <@then>Done</@then> <@catch|err|> <if(err.name === "TimeoutError")> Took too long to fetch the data! </if> <else> Promise failed with ${err.message}. </else> </@catch> </await>await(slowPromise) timeout=5000 @then -- Done @catch|err| if(err.name === "TimeoutError") -- Took too long to fetch the data! else -- Promise failed with ${err.message}.
<include-text>
<include-text>
inlines text files into a template, escaping HTML syntax characters (<
, "
, etc.).
<include-text('./foo.txt')/>
include-text("./foo.txt")
If you do not want escaping, use <include-html>
instead.
<include-html>
Like <include-text>
, <include-html>
inlines the contents of a file. However, this tag does not escape special HTML characters.
<include-html('./foo.html')/>
include-html("./foo.html")
<html-comment>
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]
…becomes:
<!--[if IE]><script src="html-shiv.js"></script><![endif]-->
EDIT Contributors
Helpful? You can thank these awesome people! You can also edit this doc if you see any issues or want to improve it.