# Marko: an HTML-based language for reactive UI, and where it stops being the right tool

> Marko extends HTML with components, conditionals, loops and a reactivity system, then compiles the result for server or client rendering. It suits teams that want markup to stay markup and JavaScript to stay out of the template's way.

**marko-js/marko** — A declarative, HTML-based language that makes building web apps fun

- Repository: https://github.com/marko-js/marko
- Website: https://markojs.com
- Stars: 14,430 · Forks: 662
- Language: JavaScript
- License: MIT
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/marko-js-marko

## The problem Marko takes on: templates that stop looking like HTML

Most JavaScript UI frameworks ask you to learn a template dialect and then a separate mental model for when things re-render. Marko starts from the opposite direction. The README describes it as "HTML reimagined as a language for building dynamic and reactive user interfaces", and states that almost any valid HTML is valid Marko. The extensions it adds are components, conditionals, loops and a reactivity system, each documented as a reference page on markojs.com.

The audience is therefore fairly specific. If your team writes markup all day and resents translating it into render functions, Marko's premise is that the markup survives. If your team is comfortable in JSX and a hook model, the same premise buys you less. The README's example component is three lines long and contains a button, a click handler and an interpolated counter, which is the whole pitch compressed: no separate state declaration block, no dependency array.

## How the language and compiler fit together

Marko is not a runtime you drop into a page. It is a language plus a compiler, and the repository reflects that: packages/ holds the compiler and runtime packages, and the release feed shows separate versioning for @marko/compiler and @marko/runtime-tags. That split matters when you upgrade, because the compiler and the runtime can move independently.

The component model is file-based. A .marko file is a component, and the README's click-count.marko shows the shape: a <let/count=0> tag declares reactive state, and the button's onClick() handler mutates it. The template then interpolates ${count}. There is no explicit re-render call and no setState.

Conditionals and loops are handled with core tags, documented under the if/else and for anchors in the reference. The reactivity reference covers how values propagate. Because the same source compiles for server rendering and for the client, the topics list on the repository includes server-side-rendering, client-side-rendering and isomorphic, which is the design intent rather than a claim about any particular app's performance.

## Installing Marko and rendering a counter

The README gives exactly two getting-started steps: run npm init marko, then read the docs. The scaffolder is the supported entry point; there is no documented manual install sequence in the README beyond that.

```bash
npm init marko
```

After the scaffolder finishes, the project's docs at markojs.com/docs/introduction/getting-started are where the README sends you. The README does not describe the prompts the scaffolder asks or the directory layout it produces.

The first component worth writing is the one from the README, a button that counts its own clicks. Save it as click-count.marko:

```marko
<let/count=0>
<button onClick() { count++ }>
  Clicked ${count} times
</button>
```

The <let/count=0> tag is the state declaration. The onClick() attribute takes a function body directly, so count++ mutates the declared value. The interpolation ${count} is what the reader sees change in the browser. If you have used a framework where the handler must be wrapped and the state must be read through a getter, the difference is visible immediately in the file.

For a quick look without installing anything, the README links a playground at markojs.com/playground, which is the lowest-friction way to check whether the syntax suits you before touching a build pipeline.

## Where Marko is the wrong choice

The README does not document an escape hatch from the compiler. If your build pipeline needs to consume plain JavaScript modules with no compile step, Marko is not that tool, and the repository gives no alternative path.

The support surface is also narrow by design. The README lists four channels: a Discord server, Bluesky at @markojs.com, the @MarkoDevTeam Twitter account, and GitHub issues. There is no mention of a commercial support tier or a paid product behind the project. For a team that needs a vendor to call, that is a real gap in the documentation, not a hidden feature.

A third boundary is versioning. The release feed shows @marko/compiler and @marko/runtime-tags shipping on separate cadences, with runtime-tags at 6.3.51 and compiler at 5.42.5 as of the listed releases. If you pin only one of them, you are pinning half of a pair. The README does not discuss compatibility ranges between the two, so that is something to confirm against the packages themselves rather than assume.

## Marko against JSX-first frameworks

The honest comparison is with any framework where the template is a JavaScript function returning elements. In that model, a conditional is a ternary or an && expression, and a loop is .map(). In Marko, those are if/else and for tags in the reference documentation, and the template stays declarative markup.

The practical difference shows up in review. A JSX file mixes control flow, data shaping and markup in one expression tree, which is flexible and easy to overuse. A .marko file keeps control flow in tag form, which constrains what a template can do but makes the markup scannable. Neither is strictly better; they fail in different directions. JSX fails toward dense functions, Marko fails toward needing a tag for something you could have written in one line of JavaScript.

The second difference is the compile target. Marko's topics list includes both server-side-rendering and client-side-rendering, and the README describes the language as the source for dynamic interfaces generally. A JSX framework can also render on the server, but the markup you write is JavaScript either way. Marko's claim is that the markup you write is HTML.

## Maintenance, releases and the MIT licence

The repository is not archived, and the last push was on 2026-09-19. That is two days before the date used here, so the project is being pushed to. The release feed shows @marko/runtime-tags@6.3.51 on 2026-09-10 and @marko/compiler@5.42.5 on 2026-09-10, so releases are recent as well.

Upgrade cost is the part worth planning for. The repository uses changesets (.changeset/ is a top-level entry) and the root package.json defines a change script that runs changeset add, plus a @ci:version script that runs changeset version and rewrites package.json files with oxfmt. That is a maintainer-facing workflow, but it tells you releases are generated from changeset entries, which is the mechanism behind the version bumps you see in the feed.

On licensing: the repository's LICENSE is MIT, and the README's badge row links to NPM and the project site. MIT is permissive, which means redistribution and modification are allowed under its terms. That is a statement about the licence text, not legal advice; if your organisation has rules about attribution notices or about dependencies pulled in alongside the compiler, read the LICENSE file and your own policy.

## Conclusion

Adopt Marko when your team already thinks in HTML and wants reactivity without a virtual DOM API in the template, and when you are willing to scaffold with npm init marko and read the reference docs for custom tags, if/else, for and reactivity. Do not adopt it if you need a large third-party component ecosystem, since the README points only to the project's own docs, playground, Discord and GitHub for support. Before committing, verify two things yourself: that your bundler and SSR setup accept the compiler output, and that the current package versions on npm match what the docs describe. The repository is not archived and the last push was on 2026-09-19.

## FAQ

### How do I install Marko?

The README gives one command, npm init marko, and then points to the getting-started docs at markojs.com. It does not document a manual install sequence beyond the scaffolder.

### What does a Marko component look like?

The README's click-count.marko example declares state with <let/count=0>, attaches onClick() { count++ } to a button, and interpolates ${count} in the text. Almost any valid HTML is valid Marko according to the README.

### Is Marko free to use?

The repository is licensed under MIT, which permits use, modification and redistribution under the terms of that licence. The README does not describe a paid tier or commercial support offering.

### Does Marko support server-side rendering?

The repository's topics include server-side-rendering, client-side-rendering and isomorphic, and the README describes Marko as a language for dynamic and reactive user interfaces. The README does not give a per-framework rendering comparison.

### Where do I get help with Marko?

The README lists a Discord server, Bluesky at @markojs.com, the @MarkoDevTeam Twitter account, and GitHub issues for bugs and feature requests. It also links a playground for trying the language online.

## Sources

- [License: MIT](https://github.com/marko-js/marko/blob/main/LICENSE)
- [marko-js/marko on GitHub](https://github.com/marko-js/marko)
- [Project website](https://markojs.com)
- [README](https://github.com/marko-js/marko/blob/main/README.md)
- [Releases](https://github.com/marko-js/marko/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/marko-js-marko
