diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9d52ccd..ee13139 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
# Changelog
-## Unreleased
+## 0.20.0 - 2026-10-03
### Breaking changes
@@ -18,16 +18,14 @@
### Added
+- Changes in `reload_dirs` reload only the pages whose HTML changed. The watcher broadcasts a `document` update for them instead of a `full` one. Pages carry an entity tag of their rendered HTML; on a change the dev client revalidates with `If-None-Match` and reloads unless the server answers `304 Not Modified`. Editing one post no longer reloads every open tab, and a save that does not change a page's output reloads nothing. This covers every successful HTML response that passes through `Volt.DevServer`, including pages that already load the dev client, so site generators built on Volt need no change. `Volt.HMR.document_update/2` sends the same request from other packages, and `Volt.HMR.Document` describes the contract for servers that send HTML some other way.
+- `config :volt, :server, morph: true` patches a changed page in place instead of reloading it, so scroll position, focus and JavaScript state survive edits to text and classes. The client reloads as before when scripts or stylesheets differ. `morph: [preserve: selector]` names elements owned by client code, such as mounted components, which are left alone. The client dispatches `volt:document-updated` after a patch.
+- Stylesheet updates no longer flash unstyled content. The dev client adds a `` for the updated stylesheet and removes the previous one once the new one has loaded, instead of changing `href` in place.
+- The dev client reloads the page once when several updates ask for a reload at the same time, such as a template that is also a Tailwind source.
- The dev server converts local CommonJS and UMD files, such as Phoenix's vendored `topbar.js`, to ES modules, so `import topbar from "../vendor/topbar"` works in development as it does in production builds. `.cjs` and `.cts` files, which the dev server did not serve, are converted the same way.
- Relative `watch_ignored` patterns also resolve from the project directory, so `_build/**` matches a watched directory inside `_build`.
-
- Volt's client types declare `import.meta.env`, so TypeScript projects no longer need their own `ImportMeta` declaration for `MODE`, `DEV`, `PROD` and exposed variables. `mix igniter.install volt` adds `env.d.ts` to configurations that list declaration files explicitly.
-- Stylesheet updates no longer flash unstyled content. The dev client adds a `` for the updated stylesheet and removes the previous one once the new one has loaded, instead of changing `href` in place.
-- `config :volt, :server, morph: true` patches a changed page in place instead of reloading it, so scroll position, focus and JavaScript state survive edits to text and classes. The client reloads as before when scripts or stylesheets differ. `morph: [preserve: selector]` names elements owned by client code, such as mounted components, which are left alone. The client dispatches `volt:document-updated` after a patch.
-- Changes in `reload_dirs` reload only the pages whose HTML changed. Pages carry an entity tag of their rendered HTML; on a change the dev client revalidates with `If-None-Match` and reloads unless the server answers `304 Not Modified`. Editing one post no longer reloads every open tab, and a save that does not change a page's output reloads nothing. This covers every successful HTML response that passes through `Volt.DevServer`, including pages that already load the dev client, so site generators built on Volt need no change. `Volt.HMR.document_update/2` sends the same request from other packages, and `Volt.HMR.Document` describes the contract for servers that send HTML some other way.
-- The dev client reloads the page once when several updates ask for a reload at the same time, such as a template that is also a Tailwind source.
-
### Fixed
- Collect every test generated by `test.each` and `describe.each`. Source lines were matched to tests by position, and tests without a detected line were dropped: a five-case table followed by two tests collected as two tests. Each case now maps to the line of its table, and tests registered in ways the source does not show, such as in a loop, are kept without a line.
diff --git a/README.md b/README.md
index c1b5733..bf2bd1e 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ Or add the dep manually:
```elixir
def deps do
- [{:volt, "~> 0.19"}]
+ [{:volt, "~> 0.20"}]
end
```
diff --git a/guides/introduction/getting-started.md b/guides/introduction/getting-started.md
index e588964..cf20852 100644
--- a/guides/introduction/getting-started.md
+++ b/guides/introduction/getting-started.md
@@ -7,7 +7,7 @@ mix igniter.install volt
```
The installer:
-- Adds `{:volt, "~> 0.19"}` to `mix.exs`
+- Adds `{:volt, "~> 0.20"}` to `mix.exs`
- Configures build settings in `config/config.exs`
- Adds lint config to `config/config.exs`
- Adds the `Volt.Formatter` plugin and formatter options to `.formatter.exs`
@@ -28,7 +28,7 @@ Add Volt to your dependencies:
```elixir
def deps do
- [{:volt, "~> 0.19"}]
+ [{:volt, "~> 0.20"}]
end
```
diff --git a/guides/migration/from-esbuild.md b/guides/migration/from-esbuild.md
index c1a6681..819d1c9 100644
--- a/guides/migration/from-esbuild.md
+++ b/guides/migration/from-esbuild.md
@@ -22,7 +22,7 @@ Remove from `mix.exs`:
Add:
```elixir
-{:volt, "~> 0.19"}
+{:volt, "~> 0.20"}
```
### 2. Replace Config
diff --git a/mix.exs b/mix.exs
index 58427bf..2f840a9 100644
--- a/mix.exs
+++ b/mix.exs
@@ -1,7 +1,7 @@
defmodule Volt.MixProject do
use Mix.Project
- @version "0.19.4"
+ @version "0.20.0"
@source_url "https://github.com/elixir-volt/volt"
def project do