marko-5-to-6-migration
Marko 5 → Marko 6 Migration
Marko 6 replaces the Class API (class {} blocks, component.js files, this.state, this.emit, legacy widgets) with the Tags API (<let>, <const>, tag variables, function-valued event attributes). marko@6 is not backward compatible: it cannot compile Class API files at all. marko@5 is forward compatible: through the interop layer built into modern marko@5, one app compiles Tags API and Class API files side by side, classifying each .marko file individually.
That gives two supported migration strategies. Confirm with the user which one they want before editing code (unless they already said):
| Strategy | End state | Choose when |
|---|---|---|
| Full migration | marko@6 installed; zero Class/widget API anywhere |
Small-to-medium codebase, you control all .marko sources, one big change is acceptable |
| Incremental migration | Stays on latest marko@5; files convert to Tags API one at a time via interop; app is shippable after every step; ends with the same final flip as the full migration |
Large codebase, must keep shipping, third-party Marko 5 tag libraries in use, multiple teams |
Hard constraints that force the choice:
- A dependency that ships Class API tags you cannot fork (e.g.
@ebay/ebayui-core) blocks the final flip tomarko@6. Use the incremental flow and stay onmarko@5until every dependency has a Tags API release. For a small straggler module, converting it locally and carrying the diff withpatch-packageis often practical — patch, verify, and upstream the conversion. - Legacy widget usage (
marko-widgets,@marko/compat-v4,w-bind,defineComponent) has no interop with Marko 6 and no automated path. Those components must be rewritten to Tags API (or at minimum to the Class API first) in either flow.
There is no automatic source-to-source converter from Class API to Tags API. Every file is converted by hand (by you), using the mapping tables in this skill.