Marko is HTML re-imagined as a language for building dynamic and reactive user interfaces. Just about any valid HTML is valid Marko, but Marko extends the HTML language to 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.
Note: Text at the root of a template (outside any tags) must be prefixed with the concise syntax's
--
to denote it is text. The parser starts in concise mode and would otherwise try to parse what you meant to be text as a concise tag declaration.-- Root level text-- Root level text
As you might expect, Marko supports all native HTML/SVG/whatever tags and attributes. In addition to these, it also comes with a set of useful core tags. Beyond this, you can also build your own custom tags and install third-party tags from npm
.
All of these types of tags use the same syntax:
<my-tag-name/>
my-tag-name
You don't need to import tags. Marko discovers them based on the folder structure—similar to how you don't specify a full path when referencing a module in node_modules/
. Marko looks in components/
by default and this directory can be configured in marko.json
.
You can use placeholders (${}
) to insert a value into the template: Placeholders accept any JavaScript expression and the result of the expression will be inserted into the HTML output:
<div>Hello</div>
div -- Hello
These values are automatically escaped so you don't accidentally insert malicious code. If you do need to pass unescaped HTML, you can use $!{}
:
<div>Hello</div>
div -- Hello <b>World</b>
ProTip: If necessary, you can escape
$
using a backslash to have it be treated as text instead of a placeholder token:<div>Placeholder example: <code>\${someValue}</code></div>div-- Placeholder example:code -- \${someValue}
In marko attributes are parsed as JavaScript expressions (instead of just strings).
<div class=myClassName/><input type="checkbox" checked=isChecked/><custom-tag string="Hello"/><custom-tag number=1/><custom-tag template-string=`Hello `/><custom-tag boolean=true/><custom-tag array=1, 2, 3/><custom-tag object={ hello: "world" }/><custom-tag variable=name/><custom-tag function-call=user.getName/>
div class=myClassNameinput type="checkbox" checked=isCheckedcustom-tag string="Hello"custom-tag number=1custom-tag template-string=`Hello `custom-tag boolean=truecustom-tag array=1, 2, 3custom-tag object={hello: "world"}custom-tag variable=namecustom-tag function-call=user.getName
Attributes that are passed to a custom tag are received as it's input
.
Note: Although in most cases you won't see a difference, strings are parsed as JavaScript strings, not HTML strings. Where this comes up most often is using the
pattern
attribute with the<input>
tag: you need to "double escape" your regex escape sequences much like you were passing a string to theRegExp
constructor (or you can use a literal/regex/
).Marko Source:<input pattern="\\w+" type="text"/><input pattern=/\w+/ type="text"/>input pattern="\\w+" type="text"input pattern=/\w+/ type="text"HTML Output:<input pattern="\w+" type="text"/>
Any JavaScript expression is a valid attribute value, provided it meets the following criteria:
It does not contain any spaces
It does not contain any right angle brackets (>
)
<custom-tag sum=1+2 difference=3-4/>
custom-tag sum=1+2 difference=3-4
Spaces and >
are contained within matching ()
, []
, {}
, strings and regexps
<custom-tag sum=1 + 2 difference=3 - 4 greater=1 > 2/>
custom-tag sum=1 + 2 difference=3 - 4 greater=1 > 2
HTML defines the following rules for boolean attributes:
The presence of a boolean attribute on an element represents the true value, and the absence of the attribute represents the false value.
In Marko when an attribute value evaluates to false
, null
, or undefined
, the attribute is not included in the output. If an attribute value is true
, only the attribute name is included in the output.
<input type="checkbox" checked=true><input type="checkbox" checked=false>
input type="checkbox" checked=trueinput type="checkbox" checked=false
Renders the following HTML:
Similarly, when only an attribute name is defined, it is equivalent to specifying the attribute with a value of true
:
<!-- These are equivalent --><custom-menu expanded><custom-menu expanded=true>
ProTip: You can take advantage of the way Marko handles boolean attributes to conditionally render attributes:
Marko Source:<div class=active && "tab-active">Hello</div>div class=active && "tab-active" -- HelloWith a value of
true
foractive
, the output would be the following:HTML Output:HelloWith a value of
false
foractive
, the output would be the following:HTML Output:Hello
The spread syntax (...
) can be used to merge in an object as attributes to a tag:
<a ...attrs target="_blank">eBay</a>
a ...attrs target="_blank" -- eBay
With attrs
as the following value:
class: "active"href: "https://ebay.com/"
would output the following HTML:
eBay
ProTip: With spread attributes order matters. You can take advantage of this to implement both default attributes, and enforced attributes.
<custom-tag ...defaults ...userSupplied class="overridden"/>custom-tag ...defaults ...userSupplied class="overridden"
ProTip: You can provide
undefined
to a spread attribute which will output nothing.
You can pass a string as the value of style
just as you would in HTML, in addition Marko supports passing an object or array as the value of the style
attribute:
<!-- string: --><div style="display:block;margin-right:16px"/><!-- object: --><div style={ display: "block", color: false, marginRight: 16 }/><!-- array: --><div style="display:block", null, { marginRight: 16 }/>
// string:div style="display:block;margin-right:16px"// object:div style={display: "block",color: false,marginRight: 16}// array:div style="display:block",null,{marginRight: 16}
In all cases, the output will be the same:
The class
attribute also supports receiving an object or array (in addition to a string) as shown below:
<!-- string: --><div class="a c"/><!-- object: --><div class={ a:true, b:false, c:true }/><!-- array: --><div class="a", null, { c:true }/>
// string:div class="a c"// object:div class={a: true,b: false,c: true}// array:div class="a",null,{c: true}
In all cases, the output will be the same:
Marko provides a shorthand for declaring classes and ids on an element:
<div.my-class/><span#my-id/><button#submit.primary.large/>
div.my-classspan#my-idbutton#submit.primary.large
Renders the following HTML:
When a tag renders its body content, it may provide data which can be received by defining parameters after the tagname. Parameters are available to the tag's body content.
This is a powerful feature that allows components to provide functionality and data while giving you full control over what gets rendered.
In the following example, <mouse>
provides a parameter which we have named position
:
<mouseposition>The mouse is at , !</mouse>
mouseposition -- The mouse is at , !
<mouse>
would render its body and provide the position similar to this:<${input.renderBody} x=0 y=0/>
.
ProTip: Tag
|parameters|
are treated as regular JavaScript function parameters. This means you can destructure, set default values, etc.<mouse{ x, y }>The mouse is at , !</mouse>mouse{ x, y } -- The mouse is at , !
Note: Parameters are not available to attributes, only to the tag body.
<mouseposition something=position>ReferenceError when setting the "something" attribute</mouse>mouseposition something=position-- ReferenceError when setting the "something" attribute
Parameters are used by some of Marko's core tags like the <for>
and <await>
tags.
Some tags and attributes accept javascript style arguments
. Arguments are denoted by parenthesis following the tag or attribute name. Arguments provide a way to pass unnamed data to a tag.
<iftrue><strong>Marko is awesome</strong></if><h1 body-only-ifskipHeading)>Conditional display heading, but always show content!</h1>
iftrue-- <strong>Marko is awesome</strong>h1 body-only-ifskipHeading)-- Conditional display heading, but always show content!
Arguments are used by some of Marko's core tags like the <if>
tag and body-only-if
attribute displayed above.
Previously you could also use them in your own custom tags however it is now recommended to use dynamic attributes.
The <${dynamic}>
syntax is used to render a tag or component that isn't determined until runtime. It can also be used within a custom tag to render body content that was passed to that tag.
< href=href>Click me!</>
< href=href>Click me!</>
With href
as https://ebay.com
would output the following HTML:
Click me!
And with href
as undefined
would output the following HTML:
Click me!
ProTip: If you find that you have a wrapper element that is conditional, but whose body should always be rendered then you can use a null dynamic tag. For example, to only render a wrapping
<a>
tag if there is a valid URL then you could do the following:Marko Source:< href=input.linkUrl >Some body content</>< href=input.linkUrl>Some body content</>Given a value of
"http://localhost/"
for theinput.linkUrl
variable: , the output would be the following:HTML Output:Some body contentGiven a value of
undefined
for theinput.linkUrl
variable: , the output would be the following:HTML Output:Some body content
Instead of just strings, the dynamic tagname can also be a component:
importimport</>
importimport</>
ProTip: You can also switch between a normal HTML tag and a component:
import<>Button text</>import<>Button text</>
Note: You cannot reference a Marko custom tag using a name string:
Marko Source:<>Button text</><>Button text</>With
isFancy
astrue
would output the following HTML:HTML Output:<fancy-button>Button text</fancy-button>fancy-button -- Button text
When a custom tag receives body content, it is passed as a renderBody
property. To render this content you can pass the renderBody
as the dynamic tagname.
<div class="container"></></div>
div class="container"</>
As the name implies, <@attribute-tags>
are special attributes that take the form of tags. They allow you to pass named body sections to a custom tag.
The core <await>
tag allows you to pass multiple body sections that it will conditionally render based on the state of the promise.
<awaitsomePromise><@thenresult>The promise resolved:</@then><@catcherror>The promise rejected:</@catch></await>
awaitsomePromise@thenresult -- The promise resolved:@catcherror -- The promise rejected:
These body sections are also commonly used to create layouts:
<page-layout><@heading><h1>Hello</h1></@heading><@body><p>Lorem ipsum....</p></@body></page-layout>
page-layout@headingh1 -- Hello@bodyp -- Lorem ipsum....
These tags are passed to the custom tag as objects with a renderBody
, it can then render its body content.
Note: Attribute tags can have their own parameters, but like attributes, they cannot access the parameters of their parent tag:
<listitem><@separator> (oops, ReferenceError)</@separator></list>listitem-- <@separator> (oops, ReferenceError)</@separator>
To execute JavaScript in your template you can insert a Javascript statement using the $ <code>
syntax.
A line that starts with a $
followed by a space will execute the code that follows.
<div>Hello,</div>
div-- Hello,--
A statement may continue onto subsequent lines if new lines are bounded by {}
, []
, ()
, ``
, or /**/
:
Multiple statements or an unbounded statement may be used by wrapping the statement(s) in a block:
ProTip: Any JavaScript statement can be used here, even
debugger
:<div></div>div--
ProTip: If necessary, you can escape
$
using a backslash to have it be treated as text instead of a placeholder token:<p>You can run JS in a Marko template like this:</p><code>\$ var num = 123;</code>p -- You can run JS in a Marko template like this:code -- \\$ var num = 123;
ProTip: If you find yourself writing a lot of inline JS, consider moving it out to an external file and then
import
it.
Inline JavaScript will run each time your template is rendered, but the JavaScript code that follows static
will only run once when the template is loaded. It must be declared at the top level and does not have access to values passed in at render time.
staticstaticstatic<div></div>
staticstaticstaticdiv --
Like inline Javascript, multiple statements or an unbounded statement may be used by wrapping the statement(s) in a block:
static
static
The import
statement is used to access data and functions from external files. It follows the same syntax as the JavaScript import
statement.
import<div>The sum of 2 + 3 is </div>
importdiv -- The sum of 2 + 3 is
The from
can also be wrapped in <angle-brackets>
which will resolve the tag inside the angle brackets using Marko's tag discovery. This can be useful when rendering a dynamic component.
import< href=input.href>Open Link</>
import< href=input.href>Open Link</>
ProTip: As a s
Marko Source:<div class=active && "tab-active">Hello</div>div class=active && "tab-active" -- HelloWith a value of
true
foractive
, the output would be the following:HTML Output:HelloWith a value of
false
foractive
, the output would be the following:HTML Output:Hello
Standard HTML comments can be used and will be stripped out of the rendered output. At the top level of the template JavaScript comments (// comment
and /** comment */
) can also be used.
<!-- This is a comment that will not be rendered --><h1>Hello</h1>
// This is a comment that will not be renderedh1 -- Hello
If you would like for your HTML comment to show up in the final output then you can use the html-comment
core tag.
Helpful? You can thank these awesome people! You can also edit this doc if you see any issues or want to improve it.