Migrating from v4 to v5
This guide is for upgrading from the last stable v4 release (v4.18.3) to v5.
Before you start
Update Node.js to v24.0.0 or later. Required for every Markuplint v5 package. See Node.js.
Changes that give you no deprecation warning
Almost every renamed or split rule still works under the old name until v6. These do not:
- Raw (non-preset)
permitted-contents,no-refer-to-non-existent-id, orlabel-has-control— the name stayed, so split-off checks are dropped with no alias warning. Preset holes:html-standardalone lacksno-broken-fragment-link;a11yalone lackslabel-no-multiple-controls. - Three table-model checks escalate
warning→error:no-table-cell-overlap,no-table-span-overflow,no-empty-table-track.
Details: Renames and Splits.
New markup-level checks in v5 (no config action, no deprecation warning)
v5 also tightened several checks unrelated to attribute values (covered separately in invalid-attr). None of these need a config change to fire, and none have a v4 equivalent to deprecate:
no-prohibited-naming: autonomous custom elements (<x-y>, nois=) without an explicitrolecan no longer carryaria-label/aria-labelledby/aria-braillelabel. Add a role that supports naming, or drop the attribute.element-supports-aria-prop: three contextual ARIA constraints (#3735) — elements whose html-spec entry setsproperties: false(e.g.<input type="hidden">) reject everyaria-*attribute;aria-expandedis now disallowed onbutton[popovertarget](the state is implicit) and onsummaryinsidedetails.permitted-contents: MathML elements enforce their exact child count (e.g.mfracrequires exactly two children); nested SVG<a>is rejected (SVG2 §17.6); a<div>inside<dl>now allows only onedt+/dd+ group — repeat groups directly under<dl>, one<div>per group.
Procedure
- Install Node.js v24+. Update CI. See Node.js.
- Upgrade
markuplintand every@markuplint/*package to the same v5 version. - Apply Framework package moves if you used htmx or Alpine.
- Run Markuplint once. Renamed or split rules report their replacements:
Rule "table-row-column-alignment" is deprecated and will be removed in v6.
Use no-table-cell-overlap, no-table-span-overflow, no-empty-table-track, consistent-table-row-length instead.
Rewrite from those lines. That does not cover the silent gaps above; check Renames and Splits by hand.
- If CI treated warnings as failures, add
--no-allow-warnings. See CLI. - If you used
--config, confirm it no longer merges with.markuplintrc. - If you used
extendswith array rule values or nestedoptions, see Config.
AI-assisted migration
With Claude Code:
npx skills add markuplint/markuplint@migrations/v4-v5
For users
| Area | Summary | Who's affected |
|---|---|---|
| Node.js | Minimum version v24.0.0 (v4 documented v18.18.0). TypeScript target ES2022. | Everyone |
| CLI | --fix-dry-run. Warnings allowed by default (--no-allow-warnings restores v4). --config does not merge. | CLI, CI |
| Config | ruleCommonSettings, named nodeRules, array override, shallow option merge, pretender restrictions, :closest() deprecated. | Config authors |
| ARIA | Default ARIA 1.3. wai-aria expands to 21 rules; some checks that were off or absent in v4 now run. | Everyone |
| Framework | @markuplint/htmx-parser removed. Alpine spec is @markuplint/alpine-spec. | htmx / Alpine.js |
| AST | Bundled parsers (html, vue, ejs, astro, mdx, jsx, svelte) now keep / in unquoted attribute values instead of splitting on it. pug is unaffected. | Users of those parsers |
Rules
| Page | Summary | Who's affected |
|---|---|---|
| Renames and Splits | Start here. 12 renames, 10 splits, wai-aria → 21, silent gaps, severity and presets. | Everyone |
| invalid-attr | Four rules. { type: X } wrapper removed. | allowAttrs / disallowAttrs |
| required-element | Renamed to require-element. Ghost elements ignored by default. | required-element users |
| deprecated-element | no-obsolete-element (error) and no-deprecated-element (warning). | deprecated-element users |
| table-row-column-alignment | Four rules; three escalate to error. | Tables with colspan / rowspan / <col> |
| parse-error | Non-fatal HTML parse errors via severity.parseError. Off by default. | Opt-in parse linting |
| textlint | @markuplint/rule-textlint removed. | Former textlint rule users |
For developers
| Area | Summary | Who's affected |
|---|---|---|
| Rule Fix Function | Per-violation fix. v4 --fix was a no-op for bundled rules. | Custom rule authors |
| API | Legacy exec() export removed. FixSummary added. | Node.js API |
| AST | Token fields renamed. Parser types removed. | Parser plugins |
tip
If you only use Markuplint through the CLI or CI, you can skip the developers section.