---
applyTo: "**/*.marko"
---
# Marko Overview
Marko uses an **HTML-like Tags API** (not JSX) with embedded JS/TS expressions. It is a **superset of well-formed HTML**.
**Examples:**
```marko
// tags/Layout.marko
import styles from "./styles.module.css";
<div class=styles.layout>
<header>
<${data.header}/>
</header>
<main>
<${data.body}/>
</main>
<footer>
<${data.footer}/>
</footer>
</div>
```
```marko
import styles from "./styles.module.css";
<let/name="World">
<Layout>
<@header>
<h1 class=styles.heading>Welcome ${name}!</h1>
</@header>
<@body>
<!-- Also use `id/` with `aria-` attributes for accessibility -->
<id/nameId>
<label for=nameId>Name:</label>
<input type="text" id=nameId value:=name>
</@body>
<@footer>
<p class=styles.footer>© 2023 My Site</p>
</@footer>
</Layout>
```
```marko
// Run once on the server
server import { initDb, queryData } from "~/utils";
server initDb("users", "comments");
<try>
<@placeholder>Loading...</@placeholder>
<@catch|err|>
<p>Error: ${err.message}</p>
</@catch>
<!-- ALWAYS use the `<await>` tag to handle async data -->
<await|users|=queryData("users", $signal)>
<for|{ id, name, age }, index| of=users by="id">
User ${index}: <Profile id=id name=name age=age/> // <Profile> is an autodiscovered custom component and does not need to be imported
</for>
</await>
<await|posts|=queryData("posts", $signal)>
<for|post| of=posts.filter(p => p.date > Date.now() - 1000*60*60*24) by(p) { return `${p.user}-${p.date}` }>
<Post ...post/>
</for>
</await>
</try>
```
```marko
// tags/online.marko
// Encapsulate logic in "renderless components" (similar to hooks)
<let/online=true>
<!-- A RARE case where you might use `<script>` -->
<script>
window.addEventListener("online", () => online = navigator.onLine), { signal: $signal });
window.addEventListener("offline", () => online = navigator.onLine), { signal: $signal });
</script>
<return online/>
```
```marko
// Then use the renderless component in your templates
<online/isOnline/>
<h1>Network Status: ${isOnline ? "Online" : "Offline"}</h1>
```
```marko
<let/count:=input.count>
<const/doubleCount=count * 2>
<log=doubleCount> // console.log(doubleCount) on initial render and each time `doubleCount` changes
<debug=count> // debugger; when the component renders and each time `count` changes
// Define an inline component with scoped CSS
<define/CustomButton|attrs|>
<style/styles>
.fancy { color: blue; }
</style>
<button ...attrs class=[styles.fancy, attrs.class]>
<${attrs.content}/>
</button>
</define>
<CustomButton onClick() { count++ }>
Increment Count
</CustomButton>
<CustomButton onClick() { count = 0 }>
Reset
</CustomButton>
<if=count % 2 === 0>
<p>Even count</p>
</if>
<else>
<debug/> // debugger; when this branch renders
<p>Odd count</p>
</else>
```
```marko
// Integrate with third-party libraries
import Map from "map";
<let/x=0>
<let/y=0>
<div/mapTarget/>
<!-- A RARE case where you might use `<lifecycle>` for imperative code -->
<lifecycle
onMount() {
this.map = new Map(mapTarget());
this.map.setCoords(x, y);
}
onUpdate() {
this.map.setCoords(x, y);
}
onDestroy() {
this.map.destroy();
}>
```
```marko
<!-- ALWAYS use the `<await>` tag to fetch data -->
<await|{ ingredients }|=fetch(`/recipies/${input.id}`), { signal:$signal })>
<IngredientsList ingredients=ingredients/>
</await>
```
```marko
<!-- NEVER wrap attributes in `{}` -->
<my-tag str="Hello"/>
<my-tag str=`Hello ${name}`/>
<my-tag num=1 + 1/>
<my-tag date=new Date()/>
<my-tag fn=function myFn(param1) {
console.log("hi");
}/>
<my-tag method(param1) {
console.log("hi");
}/>
```
```marko
<!-- use static to define values that don't change or close over reactive variables -->
static const handleClick1 = () => {
console.log("Button clicked");
};
<!-- use <const> tag to define functions that close over reactive variables -->
<const/handleClick2() {
console.log(input.message);
}>
<button onClick=handleClick1>Click Me</button>
<button onClick=handleClick2>Click Me for a message</button>
```
## Key Concepts
- **Attributes**: JS/TS expressions without `{}`.
- **Default value attribute**: `<tag=123>` is shorthand for `<tag value=123>`
- **Reactivity**: Use `<let>` for state, `<const>` for derived values. All template variables (`input`, etc.) are reactive.
- **Dynamic Tags**: `<${TagName}(args)>` syntax for dynamic components.
- **Scoped CSS**: `<style/var>` enables CSS Modules. Supports preprocessors via extension (e.g. `<style.scss>`).
- **Element Refs**: Native tags support `ref()` via `<div/ref/>`.
- **Events**: `onClick`, etc. attached directly as attributes.
- **Controllable Components**: Use `value:=state` (short for `value=state valueChange(val){ state=val }`) to control `<let>`, `<input>`, `<select>`, etc.
- **Static JS/TS Keywords**: `import`, `export`, `static`, `server`, `client` can be used to write top-level JS that executes outside the component instance (`server`/`client` control what env it runs in)
- **Custom tags**: auto-discovered from `tags/` directories and do not need to be imported
## Reference
### Core Tags
- `<let>`, `<const>`, `<return>`, `<id>`, `<log>`, `<debug>`, `<lifecycle>` (void)
- `<if>`, `<else>`, `<for|item| of=list>` `<await|val|=promise>`, `<try>`
- `<define>`
- `<script>`, `<style>`
### Controllable tags
- `<let>` supports `valueChange(value)` - useful to binding to `input` to make your own controllable components
- `<input>`, `<select>`, and `<textarea>` support `valueChange(value)`
- `<input type="checkbox">` and `<input type="radio">` support `checkedValueChange(value)`
- `<details>` and `<dialog>` support `openChange(open)`
### Enhanced `value` attributes
- `<input type="radio">` and `<input type="checkbox">` support a `checkedValue=` attribute
- `<select>` supports a `value=` attribute that can be set to a string or an array of strings (for `<select multiple>`)
- `<textarea>` supports a `value=` attribute that can be set to a string instead of providing the content as child text nodes
### Enhanced `class` & `style`
```marko
<div class="a c"/>
<div class={a: true, b: false, c: true}/>
<div class=["a", null, { c: true } ]/>
<div style="display:block;margin-right:16px"/>
<div style={ display: "block", color: false, "margin-right": 16 }/>
<div style=["display:block", null, { "margin-right": 16 }]/>
```
## Anti-patterns
### Avoid the old Marko Class API
- NEVER write class components. The OLD top-level `class { ... }` block is NOT available → use the NEW core tags and template variables to manage state and other functionality
- NEVER use the OLD `data`, `state`, `component`, and `out` → ONLY `input` (data passed to the template), `$signal` (AbortSignal for the current reactive expression), and `$global` (global render data) are available
- NEVER use the OLD tags `<while>`, `<macro>`, `<include>`
- NEVER use the OLD `key` attribute → use the NEW `by` attribute on `<for>` loops to ensure stable identity
### Marko is not JSX/React/Vue/etc.
- NEVER wrap attributes in `{}`
- NEVER prefix names with `use`
- NEVER use `<template>` to wrap top-level content
- NEVER use `<script>` to define functions or variables for use in the template → use `<const>` instead
### Other anti-patterns
- NEVER use `<let>` for constants → use `<const>`
- NEVER use `<const>` for mutable state → use `<let>`
- NEVER use `<script>` for reactive computation → use `<const>`
- NEVER use `<script>` or lifecycle methods to await data → use `<await>`
- NEVER mutate reactive values (eg. `items.push(4)`) → assign a new immutable value (eg. `items = items.concat(4)`)
- NEVER access `input` or other reactive variables in top-level `static`/`server`/`client` blocks as this is a ReferenceError
- DO NOT use `<script>` or `<lifecycle>` unless there is no alternative
- Not every form needs to use onSubmit handlers → use `<form>` with `action=` and `method=` attributes for simple forms