diff --git a/.changeset/html-plugins-to-native-html.md b/.changeset/html-plugins-to-native-html.md
new file mode 100644
index 0000000..cff1bc8
--- /dev/null
+++ b/.changeset/html-plugins-to-native-html.md
@@ -0,0 +1,7 @@
+---
+"@webpack/html-plugins-to-native-html": major
+---
+
+Add a codemod migrating html-webpack-plugin and html-loader to webpack 5.112.0+ native HTML support and minification. Translate supported minification options, preserve existing configuration and still-used dependencies, and leave unsupported template/multi-page setups for manual migration.
+
+Apply template and dependency changes through AST workflow steps coordinated by shared state, without filesystem APIs or permissions; preserve dry-run behavior and idempotence.
diff --git a/.gitattributes b/.gitattributes
index f022253..4339578 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -3,3 +3,4 @@
# CRLF fixtures exercise the editor's EOL detection — keep their bytes as-is.
codemods/*/tests/crlf/* -text
+codemods/html-plugins-to-native-html/tests/template-chunks/crlf/* -text
diff --git a/README.md b/README.md
index 120eb19..c16dc66 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,7 @@ npx codemod run @webpack/
| Codemod | Description |
| ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
| [`css-plugins-to-native-css`](codemods/css-plugins-to-native-css) | Migrate `mini-css-extract-plugin` and `style-loader`/`css-loader` rules to webpack's native CSS support (`experiments.css`). |
+| [`html-plugins-to-native-html`](codemods/html-plugins-to-native-html) | Migrate `html-webpack-plugin` and `html-loader` to native HTML support and minification. |
## Contributing
diff --git a/codemods/html-plugins-to-native-html/README.md b/codemods/html-plugins-to-native-html/README.md
new file mode 100644
index 0000000..49c4e8a
--- /dev/null
+++ b/codemods/html-plugins-to-native-html/README.md
@@ -0,0 +1,150 @@
+# @webpack/html-plugins-to-native-html
+
+Migrates webpack configurations from `html-webpack-plugin` and `html-loader` to webpack's native HTML support (`experiments.html` + `output.html`).
+
+> Requires **webpack >= 5.112.0**: the transform uses native HTML generation and the built-in HTML minifier, configured through `optimization.minimizeOptions.html`, and HTML chunk names introduced in 5.112.
+
+## What it does
+
+- Removes `new HtmlWebpackPlugin(...)` from `plugins` (and the whole `plugins` entry when it becomes empty), and the `html-webpack-plugin` `require`/`import` once it is unused.
+- Enables the native pipeline with `experiments: { html: true }` and `output.html` on each migrated configuration.
+- Sets `output.htmlFilename` to the plugin's `filename` — or to `"index.html"`, the plugin's default, since the native default is `[name].html`.
+- Maps plugin options to their `output.html` counterparts: `title`, `meta` (string values, plus `{ name | property, content }` objects — `og:*` keys included), `favicon`, `base`, `inject` (`"body"`/`"head"`/`false`; `true` is the native default), and `scriptLoading` — `"blocking"` maps directly, `"defer"` is the native default, and `"module"` becomes `output.module: true` (stable ESM output since webpack 5.111; obsolete literal `experiments.outputModule` flags are removed).
+- Uses webpack's built-in HTML minifier and translates supported `minify` options to `optimization.minimizeOptions.html`; see [Minification](#minification). Build-only options (`cache`, `showErrors`, `chunksSortMode`, `chunks: "all"`, `publicPath: "auto"`) are dropped.
+- **Multi-page setups**: several instances (or a `chunks: ["name"]` list) map to per-entry `html` descriptors — each listed entry becomes `{ import: …, html: }`, unlisted entries get no page, and `output.htmlFilename: "[name].html"` covers the per-page filenames. Requires each instance to own exactly one entry via `chunks` and no `template`; instance filenames other than `.html`/`[name].html` are flagged.
+- Options without a native equivalent (`hash`, unsupported `minify` settings, `chunks` arrays, `templateContent`, `templateParameters`, …) are dropped with a `// Removed html-webpack-plugin options without a native HTML equivalent: …` comment so you can review the behavior change; a manual migration path is appended where one exists.
+- **Companion plugins** found next to a migrated `html-webpack-plugin` instance are migrated too: `csp-html-webpack-plugin` → `output.html.csp` (its policy argument becomes `csp.policy`), `webpack-subresource-integrity` → `output.html.integrity` (`hashFuncNames` becomes the algorithm list; `enabled: false` just removes it), and `favicons-webpack-plugin` → `output.html.favicon` (a literal logo path becomes a single icon). Favicon migration always adds a review comment: generate the additional icon sizes/formats and web app manifest manually and configure their links. Native HTML does not reproduce that generation. Options beyond that are dropped with a review comment; instances whose arguments can't be understood are left in place with a comment. In template/multi-page modes options that only apply to generated pages are flagged instead.
+- Migrates `HtmlWebpackPlugin.getHooks(...)` taps to the native `webpack.html.HtmlModulesPlugin.getCompilationHooks(...)` stage covering the same moment: `alterAssetTags`/`alterAssetTagGroups` → `transformTags`, `beforeEmit` → `transformHtml`, `afterEmit` → `htmlEmitted`. The native stages take different arguments (`transformTags` hands you mutable tag descriptors instead of `data.assetTags`/head-body arrays; `transformHtml` is a waterfall on the HTML string instead of `data.html`), so each renamed tap gets a `// Review: …` comment describing the new signature — review the callback body.
+
+### `html-loader`
+
+- Removes rules that only wire up `html-loader` (cascading to empty `rules`/`module` entries): with no user rule matching `.html`, webpack's `experiments.html: "auto"` default enables native HTML by itself, and importing an `.html` file from JS natively yields the processed HTML string — the same shape `html-loader` exported.
+- Rules with extra conditions or surviving options are kept with `type: "html"` instead — and since their presence disables the `"auto"` default, `experiments.html: true` is added to that configuration.
+- Any other loader in the chain (template compilers, custom ones) keeps working in front of native HTML: it stays in `use` while `html-loader` is dropped.
+- Boolean `sources` becomes the rule's `parser: { sources }`, and static `sources.list` arrays become `parser.sources` (including `"..."`, `src` and `srcset` entries). Lists using `"..."` also normalize the global default to `module.parser.html.sources: ["..."]`, so webpack can merge the rule without expanding the boolean `true` into the list. Existing custom or disabled global source lists require manual merging and keep their loader rule.
+- Static source `filter` callbacks are adapted to native attribute maps, including callbacks reading `{ name, value }` attributes. Filters using the resource path, foreign-attribute namespace metadata or an unknown tag stay on the loader. `sources.urlFilter` maps to filters on explicit `src` lists; constant `false` disables URL extraction. Per-candidate `srcset` filtering and global URL filters over `"..."` need manual adaptation. `scriptingEnabled: true` uses native defaults; `false` has no native equivalent.
+- Inline synchronous `preprocessor` functions with a known string result become `parser.template`. The context adapter preserves dependency registration and diagnostics, and translates `resourcePath`, `resourceQuery`, `resourceFragment`, and the resource directory to the native context. Supported functions take the content and optionally the context argument and return a string literal, template literal, the content, or string operations such as `replace`/`trim` (including `const` intermediates).
+- Both literal `esModule: true` and `false` migrate: native HTML exposes the string through default imports and directly through CommonJS `require()`. Dynamic values are kept for manual review. `minimize` uses the native minifier option mapping below; unsupported loader settings keep the original configuration.
+- An identity `postprocessor` is removed. Other postprocessors are preserved: they receive a serialized JavaScript expression, whereas native `transformHtml` receives emitted HTML and cannot replace that contract.
+
+The complete public option set is checked against the [loader schema](https://github.com/webpack/html-loader/blob/main/src/options.json) and [processing pipeline](https://github.com/webpack/html-loader/blob/main/src/index.js):
+
+| Loader option | Automatic migration | Requires manual adaptation |
+| --- | --- | --- |
+| `sources` | Literal booleans and static objects | Dynamic values/spreads |
+| `sources.list` | Static `src`/`srcset` entries and `"..."` | Dynamic lists or conflicting global parser settings |
+| `sources.list[].filter` | Inline callbacks using known tags, attributes and `{ name, value }` arrays | Resource-path-dependent or namespace-sensitive callbacks; wildcard filters that read the tag name |
+| `sources.urlFilter` | Constant decisions; inline callbacks on explicit `src` lists | Default source expansion, per-candidate `srcset` filtering, or resource-path-dependent callbacks |
+| `sources.scriptingEnabled` | `true` uses native defaults | `false`: native HTML has no corresponding `