Loading...
Loading...
Use when writing or editing any .marko file, reviewing Marko 6 (Tags API) code, or when a component compiles but misbehaves — stale values, hydration/serialization errors, dead reactivity, wrong attribute/handler shapes. Covers language syntax, core tags, native-tag enhancements, TypeScript types, template API, custom-tag discovery, styling, lazy loading, and agent-error catalog. Not for routing/servers (see marko-run skill) or Marko 5 class API.
npx skill4agent add svallory/marko-ui marko6references/.mdclass { onCreate/onMount... }$ const x = ...<if(cond)><for(x in y)>on-click("handleClick")statethis.state<include><invoke>components/Marko.Component<let><const><script><lifecycle><if=cond><for|x| of=y>onClick() {}tags/export interface Input.marko<script><lifecycle><let><const>value:=<script>onClick() {}onClick=fn(event, element)currentTargetexport interface Inputmtc@marko/type-checktsc.marko<details><dialog>:haspopover<let>$signal<script><lifecycle>x = x.concat()by=<for><show><if>| File | Read when |
|---|---|
| syntax: statements, attributes & shorthands, content, attr tags, tag vars/params, dynamic tags, concise mode, whitespace |
| |
| |
| |
| |
| tag discovery order, |
| controllable components, nested state, serializable/immutable state, streaming, tooling |
skills/port-to-marko/references/marko-gotchas.mdinputtags/<tag-name><${...}>importexportstaticserverclient/x|x|input<let><const>static constcount++<let><const><script>$signal.onabort<lifecycle>onMount/onUpdate/onDestroythis<div/el/>value:=xvalue=x valueChange(v){x=v}value=input.x valueChange=input.xChange<let/x:=input.x>$globalserializedGlobals>value=(a > b)>==><div#id.cls.cls2/><Comp.x/>class="x"<${Comp.x}/>-- textaria-*aria-pressed=on && "true"null|undefined|false0""NaNnull|undefined|false|""|NaN|0n0<if><else if=…><else><else if=cond><else-if=cond><${input.content}/>content=input.content<div ...input/>content=contentinput.content<@content><Tag|params|>body</Tag><@content|params|>maincontentcontent<@item>input.item<for|it| of=input.item>[...input.item || []]Marko.AttrTag<{...}><child|{ n }|><${input.content}(1,2)/>|a, b|</></><let/x=input.x>input.x<let/x:=input.x>input.xarr.push()arr = arr.concat()obj.k = vobj = { ...obj, k: v }<let><const><if><show><for ... by="id">$signal<script><lifecycle>await<lifecycle>onMountthis<return>value=(event, element)event.currentTarget<input value:=n>value:parseFloat:=nvalueChange(v){ n = +v }<select value:=x><option>value=selected=style={}<details open:=o><dialog open:=o>showModal()openChangeexport interface Input {}interfaceMarko.Body<[params], { value }>Marko.AttrTag<T>Marko.HTML.ButtonMarko.GlobalMarko.NativeTagsmtc@marko/type-checktsctags/importmarko.jsonexportsstyle.cssfoo.style.css<style><style/styles>${}<style>import X from "<x>" with { load: "visible#id | idle?timeout=2000" }T.render(input)for await.pipe(res).toReadable()await.toString()T.mount(input, node, pos)update/destroy/value$global.serializedGlobalscspNoncerenderIdruntimeIdpackages/shadcn/ui/notes/bug-marko-dynamic-tag-hydration-crash.md<if><else><else></if><else><else-if><else><!-- right -->
<if=a>A</if>
<else-if=b>B</else-if>
<else>C</else>
<!-- wrong: wrapping breaks the sibling chain -->
<div>
<if=a>A</if>
</div>
<else>C</else>interfacetypeconstexportstaticconstfunction<!-- wrong: "interface" parses as a custom tag name -->
interface Input { name: string }
<!-- right -->
export interface Input { name: string }
<!-- wrong: bare top-level const is a template-body reactive statement, not module scope, and usually not what you meant -->
const PAGE_SIZE = 20;
<!-- right -->
static const PAGE_SIZE = 20;!/!$!{...}/<tag/name/>!/$!{expr}<!-- right: unambiguous -->
<div>$!{input.articleHtml}</div>tags/<!-- tree-node.marko -->
import TreeNode from "./tree-node.marko";
<li>
${input.label}
<if=input.children?.length>
<ul>
<for|child| of=input.children>
<TreeNode ...child/>
</for>
</ul>
</if>
</li><let>connect()<zag/api=() => mod .../>props=<zag><zag-machine>input<!-- wrong: service/api is a live object with methods — throws "Unable to serialize" -->
<let/api=switchMachine.connect(service, normalizeProps)>
<!-- right: <zag> wires the machine and returns a getter, re-evaluated on each side -->
<zag/api=() => switchMachine from=input/><const><${input.content}/><const><if><for><const>TypeError: ... is not a function@marko/runtime-tags@marko/runtime-tags@6.3.46<zag/api=...>api()connectFresh<!-- correct, at marko >=6.3.46: plain api() getter, no mirror needed -->
<zag/api=() => machine from=input/>
<span ...api().getRootProps()>
<if=api().open>...</if>
</span>calendar.markoavatar.markoradio-group.markostatic function connectFresh(...)machine.connect()<const>api()markonotes/bug-marko-dynamic-tag-hydration-crash.md<@item><for|person|>${person}<for><if>${person}value=personTypeError: Cannot read properties of undefined (reading 'person')(reading '2')'N'<${entry.content}(entry.value)/><@item|name| value=person>${name}notes/bug-marko-dynamic-tag-hydration-crash.mdscratch/content-closures-repro/JSON.stringify(input.item)[...input.item].lengthArray.isArray(input.item)Array.isArray(x) ? x : [x][...(input.item ?? [])]--<!-- wrong: each word/line is parsed as an attempted tag -->
Hello World
Welcome to Marko
<!-- right -->
-- Hello World
-- Welcome to Marko<!-- right -->
div id="hello" class="world",
style={ border: "1px solid red" }>>>==><!-- wrong: > closes the tag early -->
<if=count > 0>...</if>
<!-- right -->
<if=(count > 0)>...</if>.markoapps/docs/markocompileFilenode_modules/.bun/marko@6.3.34/node_modules/@marko/compiler/dist/index.jsbunnodemarko/translator.bun/bun <script>.mjsOK: 01-class-id-shorthand.marko -> code length 293
OK: 02-dynamic-native-tag.marko -> code length 771
OK: 03-content-forward.marko -> code length 595
OK: 04-attr-tag-collect.marko -> code length 995
OK: 05-tag-params.marko -> code length 575
OK: 05b-tag-params-parent.marko -> code length 865
OK: 06-tag-var-ref.marko -> code length 444
OK: 07-let-script-signal.marko -> code length 492
OK: 08-lifecycle-cleanup.marko -> code length 382
OK: 09-static-function.marko -> code length 651
OK: 10-if-elseif-else.marko -> code length 1459
OK: 11-export-interface-static-const.marko -> code length 635
OK: 12-self-recursive.marko -> code length 2150
OK: 13-if-paren-compare.marko -> code length 1014
OK: 14-root-text-fence.marko -> code length 270
OK: 15-leading-comma-attrs.marko -> code length 310
OK: 16-connect-closure.marko -> code length 1276
OK: 17-connect-fresh-static.marko -> code length 990interface<if><else><div><if=input.count > 0>--w1-bare-interface.marko -> "Invalid attribute name." (bare `interface` parsed as a tag)
w2-if-else-wrapped.marko -> "The <else> tag must have a preceding <if=cond> or <else if=cond>."
w3-unparenthesized-if.marko -> "Ambiguous \">\" in attribute... wrap it in parentheses, eg \"=(input.count > 0)\"."
w4-root-text-no-fence.marko -> "Unable to find entry point for custom tag <Hello>... prefix it with -- ... or wrap it in an element"