;
+ namespace Run {
+ type Context = $.ContextForFile
;
+ }
+
+ /** @deprecated use `Run` namespace instead */
+ namespace MarkoRun {
+ export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
+ export type Route = $.Routes["/learn"];
+ export type Context = $.MultiRouteContext;
+ export type Handler = $.HandlerLike;
+ export type GET = $.HandlerLike;
+ export type HEAD = $.HandlerLike;
+ export type POST = $.HandlerLike;
+ export type PUT = $.HandlerLike;
+ export type DELETE = $.HandlerLike;
+ export type PATCH = $.HandlerLike;
+ export type OPTIONS = $.HandlerLike;
+ }
+}
+
type L1 = $.Template<"L1", typeof import("../src/routes/+layout.marko")>;
declare module "../src/routes/+layout.marko" {
interface Input extends $.LayoutInput {}
@@ -172,7 +200,7 @@ declare module "../src/routes/+layout.marko" {
/** @deprecated use `Run` namespace instead */
namespace MarkoRun {
export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
- export type Route = $.Routes["/" | "/brand" | "/docs/newsletter" | "/docs/newsletter/april-2026" | "/docs/newsletter/february-2026" | "/docs/newsletter/january-2026" | "/docs/newsletter/june-2026" | "/docs/newsletter/march-2026" | "/docs/newsletter/may-2026" | "/docs/explanation/class-vs-tags-api" | "/docs/explanation/controllable-components" | "/docs/explanation/fine-grained-bundling" | "/docs/explanation/immutable-state" | "/docs/explanation/let-vs-const" | "/docs/explanation/nested-reactivity" | "/docs/explanation/optimizing-performance" | "/docs/explanation/separation-of-concerns" | "/docs/explanation/serializable-state" | "/docs/explanation/streaming" | "/docs/explanation/targeted-compilation" | "/docs/explanation/why-is-marko-fast" | "/docs/guide/duplicate-form-submissions" | "/docs/guide/library-integration" | "/docs/guide/low-level-apis" | "/docs/guide/marko-5-interop" | "/docs/guide/publishing-components" | "/docs/guide/styling" | "/docs/introduction/getting-started" | "/docs/introduction/installation" | "/docs/introduction/integrations" | "/docs/introduction/welcome-to-marko" | "/docs/introduction/why-marko" | "/docs/marko-run/adapters" | "/docs/marko-run/cli" | "/docs/marko-run/data-loading" | "/docs/marko-run/file-based-routing" | "/docs/marko-run/getting-started" | "/docs/marko-run/runtime" | "/docs/marko-run/typescript" | "/docs/marko-run/validation" | "/docs/marko-run/vite-plugin" | "/docs/reference/concise-syntax" | "/docs/reference/core-tag" | "/docs/reference/custom-tag" | "/docs/reference/language" | "/docs/reference/lazy-loading" | "/docs/reference/native-tag" | "/docs/reference/reactivity" | "/docs/reference/supported-environments" | "/docs/reference/template" | "/docs/reference/typescript" | "/docs/tutorial/components-and-reactivity" | "/docs/tutorial/fundamentals" | "/playground"];
+ export type Route = $.Routes["/" | "/brand" | "/docs/newsletter" | "/docs/newsletter/april-2026" | "/docs/newsletter/february-2026" | "/docs/newsletter/january-2026" | "/docs/newsletter/june-2026" | "/docs/newsletter/march-2026" | "/docs/newsletter/may-2026" | "/docs/explanation/class-vs-tags-api" | "/docs/explanation/controllable-components" | "/docs/explanation/fine-grained-bundling" | "/docs/explanation/immutable-state" | "/docs/explanation/let-vs-const" | "/docs/explanation/nested-reactivity" | "/docs/explanation/optimizing-performance" | "/docs/explanation/separation-of-concerns" | "/docs/explanation/serializable-state" | "/docs/explanation/streaming" | "/docs/explanation/targeted-compilation" | "/docs/explanation/why-is-marko-fast" | "/docs/guide/duplicate-form-submissions" | "/docs/guide/library-integration" | "/docs/guide/low-level-apis" | "/docs/guide/marko-5-interop" | "/docs/guide/publishing-components" | "/docs/guide/styling" | "/docs/introduction/getting-started" | "/docs/introduction/installation" | "/docs/introduction/integrations" | "/docs/introduction/welcome-to-marko" | "/docs/introduction/why-marko" | "/docs/marko-run/adapters" | "/docs/marko-run/cli" | "/docs/marko-run/data-loading" | "/docs/marko-run/file-based-routing" | "/docs/marko-run/getting-started" | "/docs/marko-run/runtime" | "/docs/marko-run/typescript" | "/docs/marko-run/validation" | "/docs/marko-run/vite-plugin" | "/docs/reference/concise-syntax" | "/docs/reference/core-tag" | "/docs/reference/custom-tag" | "/docs/reference/language" | "/docs/reference/lazy-loading" | "/docs/reference/native-tag" | "/docs/reference/reactivity" | "/docs/reference/supported-environments" | "/docs/reference/template" | "/docs/reference/typescript" | "/docs/tutorial/components-and-reactivity" | "/docs/tutorial/fundamentals" | "/learn/basics/components" | "/learn/basics/dynamic-content" | "/learn/basics/state" | "/learn/basics/templates" | "/playground"];
export type Context = Run.Context;
export type Handler = $.HandlerLike;
export type GET = $.HandlerLike;
@@ -209,6 +237,30 @@ declare module "../src/routes/docs/+layout.marko" {
}
}
+type L3 = $.Template<"L3", typeof import("../src/routes/learn/+layout.marko")>;
+declare module "../src/routes/learn/+layout.marko" {
+ interface Input extends $.LayoutInput {}
+ const Run: $.Namespace;
+ namespace Run {
+ type Context = $.ContextForFile & Marko.Global;
+ }
+
+ /** @deprecated use `Run` namespace instead */
+ namespace MarkoRun {
+ export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
+ export type Route = $.Routes["/learn/basics/components" | "/learn/basics/dynamic-content" | "/learn/basics/state" | "/learn/basics/templates"];
+ export type Context = Run.Context;
+ export type Handler = $.HandlerLike;
+ export type GET = $.HandlerLike;
+ export type HEAD = $.HandlerLike;
+ export type POST = $.HandlerLike;
+ export type PUT = $.HandlerLike;
+ export type DELETE = $.HandlerLike;
+ export type PATCH = $.HandlerLike;
+ export type OPTIONS = $.HandlerLike;
+ }
+}
+
type P1 = $.Template<"P1", typeof import("../src/routes/_home/+page.marko")>;
declare module "../src/routes/_home/+page.marko" {
const Run: $.Namespace;
@@ -1428,13 +1480,105 @@ declare module "../src/routes/docs/_compiled-docs/tutorial/fundamentals+page.mar
}
}
-type P54 = $.Template<"P54", typeof import("../src/routes/playground/+page.marko")>;
-declare module "../src/routes/playground/+page.marko" {
+type P54 = $.Template<"P54", typeof import("../src/routes/learn/_compiled-learn/basics/components+page.marko")>;
+declare module "../src/routes/learn/_compiled-learn/basics/components+page.marko" {
const Run: $.Namespace;
namespace Run {
type Context = $.ContextForFile & Marko.Global;
}
+ /** @deprecated use `Run` namespace instead */
+ namespace MarkoRun {
+ export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
+ export type Route = $.Routes["/learn/basics/components"];
+ export type Context = Run.Context;
+ export type Handler = $.HandlerLike;
+ export type GET = $.HandlerLike;
+ export type HEAD = $.HandlerLike;
+ export type POST = $.HandlerLike;
+ export type PUT = $.HandlerLike;
+ export type DELETE = $.HandlerLike;
+ export type PATCH = $.HandlerLike;
+ export type OPTIONS = $.HandlerLike;
+ }
+}
+
+type P55 = $.Template<"P55", typeof import("../src/routes/learn/_compiled-learn/basics/dynamic-content+page.marko")>;
+declare module "../src/routes/learn/_compiled-learn/basics/dynamic-content+page.marko" {
+ const Run: $.Namespace;
+ namespace Run {
+ type Context = $.ContextForFile & Marko.Global;
+ }
+
+ /** @deprecated use `Run` namespace instead */
+ namespace MarkoRun {
+ export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
+ export type Route = $.Routes["/learn/basics/dynamic-content"];
+ export type Context = Run.Context;
+ export type Handler = $.HandlerLike;
+ export type GET = $.HandlerLike;
+ export type HEAD = $.HandlerLike;
+ export type POST = $.HandlerLike;
+ export type PUT = $.HandlerLike;
+ export type DELETE = $.HandlerLike;
+ export type PATCH = $.HandlerLike;
+ export type OPTIONS = $.HandlerLike;
+ }
+}
+
+type P56 = $.Template<"P56", typeof import("../src/routes/learn/_compiled-learn/basics/state+page.marko")>;
+declare module "../src/routes/learn/_compiled-learn/basics/state+page.marko" {
+ const Run: $.Namespace;
+ namespace Run {
+ type Context = $.ContextForFile & Marko.Global;
+ }
+
+ /** @deprecated use `Run` namespace instead */
+ namespace MarkoRun {
+ export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
+ export type Route = $.Routes["/learn/basics/state"];
+ export type Context = Run.Context;
+ export type Handler = $.HandlerLike;
+ export type GET = $.HandlerLike;
+ export type HEAD = $.HandlerLike;
+ export type POST = $.HandlerLike;
+ export type PUT = $.HandlerLike;
+ export type DELETE = $.HandlerLike;
+ export type PATCH = $.HandlerLike;
+ export type OPTIONS = $.HandlerLike;
+ }
+}
+
+type P57 = $.Template<"P57", typeof import("../src/routes/learn/_compiled-learn/basics/templates+page.marko")>;
+declare module "../src/routes/learn/_compiled-learn/basics/templates+page.marko" {
+ const Run: $.Namespace;
+ namespace Run {
+ type Context = $.ContextForFile & Marko.Global;
+ }
+
+ /** @deprecated use `Run` namespace instead */
+ namespace MarkoRun {
+ export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
+ export type Route = $.Routes["/learn/basics/templates"];
+ export type Context = Run.Context;
+ export type Handler = $.HandlerLike;
+ export type GET = $.HandlerLike;
+ export type HEAD = $.HandlerLike;
+ export type POST = $.HandlerLike;
+ export type PUT = $.HandlerLike;
+ export type DELETE = $.HandlerLike;
+ export type PATCH = $.HandlerLike;
+ export type OPTIONS = $.HandlerLike;
+ }
+}
+
+type P58 = $.Template<"P58", typeof import("../src/routes/playground/+page.marko")>;
+declare module "../src/routes/playground/+page.marko" {
+ const Run: $.Namespace;
+ namespace Run {
+ type Context = $.ContextForFile & Marko.Global;
+ }
+
/** @deprecated use `Run` namespace instead */
namespace MarkoRun {
export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
@@ -1524,4 +1668,8 @@ type D48 = $.Meta<"D48", typeof import("../src/routes/docs/_compiled-docs/refere
type D49 = $.Meta<"D49", typeof import("../src/routes/docs/_compiled-docs/reference/typescript+meta.json")>;
type D50 = $.Meta<"D50", typeof import("../src/routes/docs/_compiled-docs/tutorial/components-and-reactivity+meta.json")>;
type D51 = $.Meta<"D51", typeof import("../src/routes/docs/_compiled-docs/tutorial/fundamentals+meta.json")>;
-type D52 = $.Meta<"D52", typeof import("../src/routes/playground/+meta.json")>;
+type D52 = $.Meta<"D52", typeof import("../src/routes/learn/_compiled-learn/basics/components+meta.json")>;
+type D53 = $.Meta<"D53", typeof import("../src/routes/learn/_compiled-learn/basics/dynamic-content+meta.json")>;
+type D54 = $.Meta<"D54", typeof import("../src/routes/learn/_compiled-learn/basics/state+meta.json")>;
+type D55 = $.Meta<"D55", typeof import("../src/routes/learn/_compiled-learn/basics/templates+meta.json")>;
+type D56 = $.Meta<"D56", typeof import("../src/routes/playground/+meta.json")>;
diff --git a/agent-feedback/bugs.md b/agent-feedback/bugs.md
index b95fa687fb..a76d2aeeef 100644
--- a/agent-feedback/bugs.md
+++ b/agent-feedback/bugs.md
@@ -1,3 +1,9 @@
# Suspected Bugs
Out-of-scope defects noticed while working on something else. Format and rules: [README.md](README.md).
+
+## `` return value never flows when client-rendered without server markup
+
+`src/tags/let/let-debounce.marko` › `Input` | 2026-07-24 | impact:med | effort:med
+
+When `` is used inside a component tree that is rendered entirely in the browser (mounted via a dynamic tag after a lazy `import()`, with no server-rendered markup to hydrate), its `` value is never assigned: the compiled parent output wires `$value`/`$delay` setters but the tag's returned binding never fires, so downstream `const`/`if`/`script` sections depending on it never run. The same usage works on `/playground` where the tree is server-rendered and hydrated (`src/routes/playground/tags/playground/tags/result/result.marko`). This looks like a `@marko/compiler`/runtime issue with `` in client-only render paths and may be worth reducing and reporting upstream. Re-verify by rendering any lazily-imported component containing `let-debounce/files=input.files` followed by `script -- console.log(files)`; the log never fires (workaround in `src/tags/app-playground/tags/embed-result.marko` uses a manual `setTimeout` debounce instead).
diff --git a/cspell.json b/cspell.json
index 1cf84c10d4..f11798ed40 100644
--- a/cspell.json
+++ b/cspell.json
@@ -47,6 +47,7 @@
"mlog",
"nbsp",
"noindex",
+ "noopener",
"onbeforeinput",
"openjsf",
"optgroup",
@@ -80,6 +81,12 @@
"WHATWG"
],
"ignoreRegExpList": [],
- "files": ["*", "docs/**/*", "src/**/*"],
- "ignorePaths": ["**/_compiled-docs", "dist", "skills", ".claude"]
+ "files": ["*", "docs/**/*", "learn/**/*", "src/**/*"],
+ "ignorePaths": [
+ "**/_compiled-docs",
+ "**/_compiled-learn",
+ "dist",
+ "skills",
+ ".claude"
+ ]
}
diff --git a/docs/tutorial/components-and-reactivity.md b/docs/tutorial/components-and-reactivity.md
index ec85cd7384..a9600ae324 100644
--- a/docs/tutorial/components-and-reactivity.md
+++ b/docs/tutorial/components-and-reactivity.md
@@ -18,7 +18,7 @@ As with many user interfaces, our first step is to gather input from the user. W
Of course, right now we aren't keeping track of the value that this input contains. To do this, we need to introduce state. In Marko, the most common way to do this is with [tag variables](../reference/language.md#tag-variables). Here, we will use [Marko's `` tag](../reference/core-tag.md#let):
-```marko
+```marko playground
@@ -29,7 +29,7 @@ Of course, right now we aren't keeping track of the value that this input contai
Now the `` has an initial value, but we still aren't keeping track of it when it changes. One way you may think to do this is by listening for [the `input` event](https://developer.mozilla.org/en-US/docs/Web/API/Element/input_event) with an [event handler](../reference/native-tag.md#event-handlers):
-```marko
+```marko playground
// Warning: There's a better way to do this!
@@ -41,7 +41,7 @@ Now the `` has an initial value, but we still aren't keeping track of it
This _seems_ to work at first glance, but you'll find out quickly that the value of the input isn't fully synchronized. This is because in HTML, `value=` actually refers to the [_default_ value](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input#value) of the input and not its current value. This is why instead, we should leverage the [controllable](../reference/native-tag.md#change-handlers) pattern with `Change` handlers.
-```marko
+```marko playground
@@ -50,7 +50,7 @@ This _seems_ to work at first glance, but you'll find out quickly that the value
Because this is such a common pattern, Marko provides a [shorthand](../reference/language.md#shorthand-change-handlers-two-way-binding) for it!
-```marko
+```marko playground
@@ -61,7 +61,7 @@ Because this is such a common pattern, Marko provides a [shorthand](../reference
Now we can use [the `` tag](../reference/core-tag.md#const) to convert to celsius!
-```marko
+```marko playground
@@ -77,7 +77,7 @@ Since `degC` is a [tag variable](../reference/language.md#tag-variables), its ch
Now that we have a reactive variable, let's see what else we can do! Maybe some notes about the temperature, using [conditional tags](../reference/core-tag.md#if--else)?
-```marko
+```marko playground
@@ -101,7 +101,7 @@ Now that we have a reactive variable, let's see what else we can do! Maybe some
Or what about a temperature gauge, with some fancy CSS?
-```marko
+```marko playground
@@ -141,7 +141,7 @@ Or what about a temperature gauge, with some fancy CSS?
Actually, this is getting a little bit too complex to all put in one place. Maybe we should pull that temperature gauge out into a component:
-```marko
+```marko playground
/* index.marko */
@@ -154,7 +154,7 @@ Actually, this is getting a little bit too complex to all put in one place. Mayb
```
-```marko
+```marko playground
/* tags/gauge.marko */
diff --git a/learn/1-basics/1-templates.md b/learn/1-basics/1-templates.md
new file mode 100644
index 0000000000..203a1d6609
--- /dev/null
+++ b/learn/1-basics/1-templates.md
@@ -0,0 +1,36 @@
+# Templates Are HTML
+
+Welcome! This is a hands-on tour of Marko. Each lesson loads files into the
+editor on the right, and everything we type compiles and runs immediately in
+the preview below it.
+
+Marko is a superset of HTML, so nearly any valid HTML is already a valid
+template. The editor starts with an event flyer written in plain HTML:
+
+```marko playground
+
Open Mic Night
+
Every Thursday at the corner cafe.
+
+
Doors at 7pm
+
Sign-ups at the bar
+
Five minutes per act
+
+```
+
+Try changing some of the markup and watch the preview update.
+
+## Attributes
+
+Attribute values are JavaScript expressions, not strings. That means template
+literals, math, objects, and function calls all work directly in markup:
+
+```marko playground
+
+
+
Open Mic Night
+
Every Thursday at the corner cafe.
+Email the host
+```
+
+Load this version and try giving the heading a different color, or computing
+one from an expression.
diff --git a/learn/1-basics/2-dynamic-content.md b/learn/1-basics/2-dynamic-content.md
new file mode 100644
index 0000000000..f43d5de81c
--- /dev/null
+++ b/learn/1-basics/2-dynamic-content.md
@@ -0,0 +1,34 @@
+# Dynamic Content
+
+Tag content works like a JavaScript template literal, so `${expressions}`
+interpolate anywhere text can appear. The `` tag gives a value a name
+that the rest of the template can use.
+
+```marko playground
+
+
+
${album.title}
+
${album.tracks} tracks, ${album.minutes} minutes
+
About ${Math.round(album.minutes / album.tracks)} minutes per track
+```
+
+Try renaming the album or changing the track count. The derived line at the
+bottom recalculates automatically because it is just an expression over the
+same value.
+
+## Lists
+
+The `` tag repeats its body for each item:
+
+```marko playground
+
+
+
Track List
+
+
+
${track}
+
+
+```
+
+Add a track to the array and it appears in the list.
diff --git a/learn/1-basics/3-state.md b/learn/1-basics/3-state.md
new file mode 100644
index 0000000000..bfe9365f0d
--- /dev/null
+++ b/learn/1-basics/3-state.md
@@ -0,0 +1,27 @@
+# Adding State
+
+So far our templates have been static. The `` tag introduces state: a
+value that can change over time, with every expression that uses it updating
+automatically.
+
+```marko playground
+
+
+
+
${volume === 0 ? "Muted" : `Playing at ${volume}%`}
+
+```
+
+Drag the slider in the preview. The `:=` shorthand keeps the input and the
+`volume` variable in sync in both directions, and the `Mute` button assigns to
+the same variable from an event handler.
+
+There is no render function and no subscription to manage. The compiler sees
+which expressions read `volume` and wires up exactly the updates needed.
diff --git a/learn/1-basics/4-components.md b/learn/1-basics/4-components.md
new file mode 100644
index 0000000000..cab9b7e5d5
--- /dev/null
+++ b/learn/1-basics/4-components.md
@@ -0,0 +1,35 @@
+# Custom Tags
+
+Marko discovers components from the file system: any template in a `tags/`
+directory can be used as a tag, no imports required. This lesson's editor has
+two files. `index.marko` renders a `` tag, and
+`tags/rating-stars.marko` defines it.
+
+```marko playground
+/* index.marko */
+
+
+