---
# System prepended metadata

title: Marko AI Instructions

---

---
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
