MX
MX (Markup eXtended) is a template language born from Marko. It takes Marko’s syntax — the syntax people already know from Marko — and brings it to wherever JSX lives today: React, Preact, Solid, Hono, Astro, and server-side HTML. MX itself defines the markup and the structural tags; each host decides what state, reactivity, and output mean.
.mx is the official and only whole-template file extension. Porting a Marko component that stays within the MX 1.0 subset is a rename.
What MX is
- The language. A markup grammar plus a small set of structural tags:
<if>/<else if>/<else>, every<for>form, attribute tags, tag params,<define>,<const>,static,import. These work identically on every host and render exactly the way Marko renders them. - Custom tags. Define project vocabulary as an MX template or a TypeScript sidecar. Calls are discovered without imports and expand to ordinary IR before any emitter runs, so one definition works on all six hosts. Start with Custom tags.
- The core. One package (
@mxlang/core) that consumes Marko’s AST, applies structural and custom-tag lowerings, and exposesHostDeclarationsplus hooks so a host can add its own stateful tags. - Targets and hosts. A target is a policy over the core; a host is a target plus the integration that makes it usable in a framework’s ecosystem, where the component stays a native component of that framework and MX takes the place of its JSX or template:
- HTML (a target, not a host) — a
.mxfile compiles to a pure(input) => stringfunction. No runtime, no framework. - Astro —
.astro.mxis an Astro component with MX as its template; plain.mxcomponents and pages render to static markup at build time, no islands, no client JavaScript. - React — a React function component with MX in place of the JSX; hooks go through
<const>and<try>through a class error boundary. - Preact — a Preact function component with MX in place of the JSX: the same structural lowering, on Preact’s runtime and native prop vocabulary.
- SolidMX (
.solid.mx) — MX in JSX’s position inside a Solid component file, lowered to Solid’s own JSX. See SolidMX.
- HTML (a target, not a host) — a
What MX is not
- Not a Marko runtime. No resume, no serialized scope data, no
@marko/runtime-tags. A host that wants client-side state uses its own framework’s mechanisms. See why MX has no resume. - Not a promise of portability for stateful templates. A template using
<let>means one thing on the html target (an error, unless explicitly allowed) and something else on a future reactive host — the same way JSX means something different depending on which framework compiles it. The structural core is portable; stateful tags are host-bound. - Not a superset of Marko. MX 1.0 is a strict subset.
The subset rule
Every MX 1.0 file is a valid Marko file, with the same meaning for the structural core. A host may forbid a tag it cannot honor — the Astro host rejects <let> because it has no reactive target — but no host may add syntax, attribute forms, or file conventions that Marko’s own parser would reject. This lets MX reuse Marko’s parser, formatting rules, and tree-sitter grammar while MX’s own integrations keep the .mx identity and apply host policy.
The rule holds until MX 2. From MX 2 on, MX may diverge from Marko, but only deliberately and one recorded step at a time. Custom tags do not break the rule: a call uses ordinary Marko tag syntax and expands only after parsing.
Divergence policy
Every deliberate divergence from Marko is recorded in a table — what changed, why, and what test guards it — before it ships. Syntax divergences land only with the tooling they affect (grammar, formatter, language server). MX 1.0 uses Marko syntax unmodified; its one deliberate semantic divergence is the consumer-declared AttrTag value shape. Two cases where the html target used to be more permissive than Marko were implementation bugs rather than divergences, and both are fixed — the Marko-parity oracle now reports no translator bugs.
Start here
If you are new to MX, read these five in order — about twenty minutes end to end:
- Structural tags —
<if>,<for>, and the rest of the portable core. This is the part that means the same thing on every host. - Interpolation and escaping —
${}versus$!{}, and the whitespace rule that surprises people coming from JSX. - Custom tags — define reusable project vocabulary without coupling it to a host.
- Pick your host and follow its install: HTML for a plain string, Astro for static markup, React or Preact for components, Hono for server JSX, SolidMX for MX inside a Solid file.
- Stateful tags — what
<let>and friends mean, which is the one place hosts deliberately disagree.
Then set up your editor: Zed, VS Code, and the TypeScript integration that type-checks MX files.
Reference
- Language — the structural tags, attribute tags, interpolation, and what a host is free to define itself.
- Custom tags — template and sidecar authoring, discovery, API types, and diagnostics.
- Architecture — how the core and hosts fit together.
- Editors — Zed, VS Code, and the diagnostics language server.
- Contributing — repo layout, the verification chain, and how to add a host.
- Divergences & MX 2 — what MX deliberately does not do yet, and why.