Try   HackMD

Marko Tags API Reference

Syntax

Marko is roughly a superset of the HTML, reimagined as a language for building dynamic and reactive user interfaces. Most .html files can also compile as .marko files, but Marko also extends the language to provide tools which allow building modern applications in a declarative way.

ProTip
Marko also supports a beautiful concise syntax. If you'd prefer to see the documentation using this syntax, just click the "Switch Syntax" button in the corner of any Marko code sample.

Map of Marko's tag syntax:

<tagName|tag,parameters|/tagVariable ...attributes>
  renderBody
  <@attrTag/>
</tagName>

Using links like this might only work in HackMD's Markdown Parser Luke

Tags

All native HTML tags are also supported in Marko. In addition, Marko comes with a set of core tags which may be used as building blocks for custom tags.

All of these types of tags use the same syntax, which is identical to HTML:

<tag-with-renderBody>
  renderBody
</tag-with-renderBody>

<self-closing-tag/>

Attributes

Attributes are mostly the same as HTML, but their contents are parsed as JavaScript expressions instead of strings. With few exceptions, this is an extension of HTML.

All of the following are valid attributes in Marko:

<my-tag
  attrString="hello"
  attrNumber=3
  attrExpression=5+7
  attrVariable=myValue
  attrList=["a", "b", "c"]
  attrObject={a: "a", b: "b"}
  attrFunction=() => {}
/>

ProTip
See the shorthands & concise mode section for a few built-in features that can make attributes easier to read.

Marko's parser is able to figure out when each expression ends and another attribute begins, so no parentheses are needed except in rare ambiguous cases such as the > operator:

<my-tag isGreater=(4 > 3)></my-tag>

Similarly to HTML, Marko parses attributes without any arguments as true:

<input type="checkbox" checked>
// identical to
<input type="checkbox" checked=true>

JavaScript-style object spreads are also allowed, where each key and its value are mapped to an attribute.

<input ...{type: "checkbox", checked: true}>

Body Content

Just like in JavaScript's `template strings`, tag bodies may use ${placeholders} to interpolate values:

<div>
  Hello ${"world".toUpperCase()}
</div>

Warning
Values inside placeholders are automatically escaped to prevent the injection of malicious code. For cases where the developer wants to insert HTML directly and is certain that code injection is not possible, an ! may be added to pass unescaped HTML:

<div>
  Hello $!{"<b>World</b>"}
</div>

Note
If necessary, the placeholder $ may be escaped with a \:

<div>
  Placeholder example: <code>\${someValue}</code>
</div>

Attribute Tags

The final way to pass attributes in Marko is with <@attribute-tags>. It is technically possible to acheive everything that Marko has to offer without using these, but they provide an alternative method for passing complicated render bodies to components:

<my-select>
  <@option>A</@option>
  <@option>B</@option>
</my-select>
// identical to
<my-select
  option=[
    { renderBody: "A" },
    { renderBody: "B" }
  ]
/>

Tag Variables

In Marko, tags may also provide a return value to the template where they are referenced. The data type of these values is in many ways similar to that of JavaScript variables, but they also trigger updates to their dependencies every time their tag provides a new value. Tag variables will be explored further for native tags, core tags and custom tags, but the syntax is always as follows:

<my-tag/tagVariable />

Tag Parameters

While passing a renderBody, tags may also specify parameters which can be passed back by the child template. These values are not the same as tag variables, because they are only accessible within the render body.

parent.marko

<child|a, b|>
    ${a}: ${b}
</child>

child.marko

<${renderBody}=[a, b]/>

Native Tags

Native tags in Marko are nearly identical to native tags in HTML, with a few additional features.

Event handlers

Event handlers such as onclick are supported in native tags, but since they only are only able to receive strings it is not possible to attach them to any of Marko's features. Instead, Marko provides special event handlers on all native tags that are compiled as JavaScript. These are accessed via special camelCase onEvent or kebab-case on-event attributes.

Each of the following examples run the function body every time the "hover" event is triggered on the button:

<button onHover=() => { /* runs on hover */ } />
<button on-hover=function() { /* runs on hover */ } />
<button onHover() { /* runs on hover */ } />

Note
Due to the nature of camelCase, the onEvent option cannot be used to handle events that begin with capital letters. To handle an Uppercase event, use on-Uppercase.

Special Case Attributes

In addition to strings, the class and style attribute accept arrays or objects that will be converted to strings automatically by Marko. The output of each of these cases is identical:

// string
<div
  class="a c"
  style="display:block;margin-right:16px"
/>

// object
<div
  class={ a: true, b: false, c: true}
  style={ display: "block", color: false, marginRight: 16 }
/>

// array
<div
  class=["a", null, { c: true }]
  style=["display:block", null, { marginRight: 16 }]
/>

Native Tag References

Every native tag provides a tag variable which acts as a getter function for a reference to its DOM object.

<div/myDiv>Hello World</div>

<div>
  The previous element says ${myDiv().innerHTML}
</div>

This is only the case for built-in HTML elements. Conceptually, native tags can be thought of as having something like <return=() => ref /> under the hood.

Custom Tags / Components

Custom tags are single-file blocks of code that are used to separate an application into encapsulated, reusable components. Tags are discovered automatically based on file name, and treated as if they were built into Marko as HTML or core tags.

components/hello.marko

<h1>Hello World</h1>

page.marko

<html>
<body>
  <hello/>
</body>
</html>

result.html

<html>
<body>
  <h1>Hello World</h1>
</body>
</html>

input

Every component file has access to a global variable called input, which acts as an object that contains its attributes.

components/hello.marko

<h1>Hello ${input.name}</h1>

page.marko

<html>
<body>
  <hello name="Marko" />
</body>
</html>

result.html

<html>
<body>
  <h1>Hello Marko</h1>
</body>
</html>

renderBody

The body content of each component is accessible in a component file as a member of the input object called renderBody:

fancy-border.marko

<div class="fancy-border">
  <${input.renderBody}/>
</div>

Core Tags

Not sure if we should do alphabetical order or order of importance. This is roughly just the order I remembered them inLuke

<if>/<else-if>/<else>

These tags are used to conditionally render information. if and else-if will render their contents when their value attribute is truthy, while else-if and else act as fallbacks for their previous conditional sibling.

<if value=arriving>
  Hey there
</if>
<else-if=leaving>
  Bye now
</else-if>
<else>
  What’s up?
</else>

ProTip
See the default value shorthand for a more concise way of writing if and else-if statements.

<for>

Just like JavaScript's for loops, the Marko <for> tag can iterate over arrays/array-likes, object properties and values, and ranges of numbers. The items being iterated over are received as tag parameters which may be used in the body content of the tag.

for-of

<for|item| of=["a", "b", "c"]>
  ${item}
</for>

for-in

<for|key, value| in={a: 1, b: 2, c: 3}>
  ${key}: ${value}
</for>

for-to

<for|num| to=5>${num}, </for>
// 0, 1, 2, 3, 4, 5, 

<for|num| from=3 to=7>${num}, </for>
// 3, 4, 5, 6, 7, 

<for|num| from=2 to=10 step=2>${num}, </for>
// 2, 4, 6, 8, 10, 

for-until ???

This doesn't exist yet but maybe adding it to the docs will be enough to convince the team it's worth including in Marko 6 Luke

<for|num| until=5>${num}, </for>
// 0, 1, 2, 3, 4, 

<for|num| from=3 until=7>${num}, </for>
// 3, 4, 5, 6, 

<for|num| from=2 until=10 step=2>${num}, </for>
// 2, 4, 6, 8, 

<let>

<let/x=4/>
<let/_x="HELLO" />
<let/x=_x valueChange(newValue) { _x = newValue.toUpperCase() } />

<const>

Declares a reactive, derived value which updates whenever any of its dependencies change.

<const/doubleX=x*2 />

Conceptually, under the hood the implementaton of the <const> tag looks like this:

const.marko

<return=input.value/>

<effect>

Declares a function which re-runs when its dependencies change

<effect() {
  console.log(doubleX);
  // will log every time `doubleX` updates
}/>

<lifecycle>

Provides code which is only run on the client, throughout the lifecycle of a component. Consistent data can be stored in this.

onMount

Fires when the component is first created

onUpdate

Identical to the <effect> tag, but with access to its lifecycle's this.

onDestroy

Fires before the component is destroyed

<lifecycle
  onMount() {
    // create utility
  }
  onUpdate() {
    // use utility
  }
  onDestroy() {
    // destroy utility
  }
/>

<return>

my-const.marko

<return=input.value/>

<id>

Generates a unique id string which may be safely referenced across the server-client boundary.

<id/inputId />
<label for=inputId>...</label>
<input id=inputId />

<html-comment>

Normal HTML comments are automatically stripped from Marko files, these will not

<html-comment>Visible in the client</html-comment>

<define>

Almost the same as the <const> tag, but it gives back a whole object instead of just value. Can be used as an alternative to file-based tags

<define/fancyButton|{renderBody}|>
  <button class="fancy"><${renderBody}/></button>
</define>

<fancyButton>Hello!</fancyButton>

Essentially, this is the tag's implementation under the hood:

define.marko

<return=input/>

Shorthands & Concise Mode

Default Value

If the first attribute in a tag doesn't have a name, Marko will automatically call it "value". Core tags take advantage of this, which is why these are equivalent:

<let/x value=1/>
<let/x=1/>

Change bindings

<let/count=0 />
<counter count:=count />
// transforms to
<counter count=count countChange(c) { count = c } />

counter.marko

<let/count:=input.count />
<button onClick() { count++ }>${count}</button>
// transforms to
<let/count=input.count valueChange=input.countChange />

class and id shorthands

Marko allows tag names to be written using a limited CSS selector syntax to include classes and ids:

<div#main.container />
// equivalent to
<div id="main" class="container" />

Concise Mode

Behavior

  • Dependency tracking of functions
    • An expression is reevaluated whenever its dependencies change
  • Resumability (render once server vs client)