diff --git a/CHANGELOG.md b/CHANGELOG.md
index af9aa228c..c4acf4a76 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
-## Unreleased
+## [0.23.0] - 2023-05-03
### Fixed
@@ -873,6 +873,7 @@ https://textual.textualize.io/blog/2022/11/08/version-040/#version-040
- New handler system for messages that doesn't require inheritance
- Improved traceback handling
+[0.23.0]: https://github.com/Textualize/textual/compare/v0.22.3...v0.23.0
[0.22.3]: https://github.com/Textualize/textual/compare/v0.22.2...v0.22.3
[0.22.2]: https://github.com/Textualize/textual/compare/v0.22.1...v0.22.2
[0.22.1]: https://github.com/Textualize/textual/compare/v0.22.0...v0.22.1
diff --git a/docs/blog/images/frogmouth.svg b/docs/blog/images/frogmouth.svg
new file mode 100644
index 000000000..685064d26
--- /dev/null
+++ b/docs/blog/images/frogmouth.svg
@@ -0,0 +1,268 @@
+
diff --git a/docs/blog/posts/release0-23-0.md b/docs/blog/posts/release0-23-0.md
new file mode 100644
index 000000000..8fbe14573
--- /dev/null
+++ b/docs/blog/posts/release0-23-0.md
@@ -0,0 +1,88 @@
+---
+draft: false
+date: 2023-05-03
+categories:
+ - Release
+title: "Textual 0.23.0 improves message handling"
+authors:
+ - willmcgugan
+---
+
+# Textual 0.23.0 improves message handling
+
+It's been a busy couple of weeks at Textualize.
+We've been building apps with [Textual](https://github.com/Textualize/textual), as part of our *dog-fooding* week.
+The first app, [Frogmouth](https://github.com/Textualize/frogmouth), was released at the weekend and already has 1K GitHub stars!
+Expect two more such apps this month.
+
+
+
+
+--8<-- "docs/blog/images/frogmouth.svg"
+
+
+!!! tip
+
+ Join our [mailing list](http://eepurl.com/hL0BF1) if you would like to be the first to hear about our apps.
+
+We haven't stopped developing Textual in that time.
+Today we released version 0.23.0 which has a really interesting API update I'd like to introduce.
+
+Textual *widgets* can send messages to each other.
+To respond to those messages, you implement a message handler with a naming convention.
+For instance, the [Button](/widget_gallery/#button) widget sends a `Pressed` event.
+To handle that event, you implement a method called `on_button_pressed`.
+
+Simple enough, but handler methods are called to handle pressed events from *all* Buttons.
+To manage multiple buttons you typically had to write a large `if` statement to wire up each button to the code it should run.
+It didn't take many Buttons before the handler became hard to follow.
+
+## On decorator
+
+Version 0.23.0 introduces the [`@on`](/guide/events/#on-decorator) decorator which allows you to dispatch events based on the widget that initiated them.
+
+This is probably best explained in code.
+The following two listings respond to buttons being pressed.
+The first uses a single message handler, the second uses the decorator approach:
+
+=== "on_decorator01.py"
+
+ ```python title="on_decorator01.py"
+ --8<-- "docs/examples/events/on_decorator01.py"
+ ```
+
+ 1. The message handler is called when any button is pressed
+
+=== "on_decorator02.py"
+
+ ```python title="on_decorator02.py"
+ --8<-- "docs/examples/events/on_decorator02.py"
+ ```
+
+ 1. Matches the button with an id of "bell" (note the `#` to match the id)
+ 2. Matches the button with class names "toggle" *and* "dark"
+ 3. Matches the button with an id of "quit"
+
+=== "Output"
+
+ ```{.textual path="docs/examples/events/on_decorator01.py"}
+ ```
+
+The decorator dispatches events based on a CSS selector.
+This means that you could have a handler per button, or a handler for buttons with a shared class, or parent.
+
+We think this is a very flexible mechanism that will help keep code readable and maintainable.
+
+## Why didn't we do this earlier?
+
+It's a reasonable question to ask: why didn't we implement this in an earlier version?
+We were certainly aware there was a deficiency in the API.
+
+The truth is simply that we didn't have an elegant solution in mind until recently.
+The `@on` decorator is, I believe, an elegant and powerful mechanism for dispatching handlers.
+It might seem obvious in hindsight, but it took many iterations and brainstorming in the office to come up with it!
+
+
+## Join us
+
+If you want to talk about this update or anything else Textual related, join us on our [Discord server](https://discord.gg/Enf6Z3qhVr).
diff --git a/docs/examples/events/on_decorator01.py b/docs/examples/events/on_decorator01.py
index 3b97f7f0d..7b9c0276e 100644
--- a/docs/examples/events/on_decorator01.py
+++ b/docs/examples/events/on_decorator01.py
@@ -12,7 +12,7 @@ class OnDecoratorApp(App):
yield Button("Toggle dark", classes="toggle dark")
yield Button("Quit", id="quit")
- def on_button_pressed(self, event: Button.Pressed) -> None:
+ def on_button_pressed(self, event: Button.Pressed) -> None: # (1)!
"""Handle all button pressed events."""
if event.button.id == "bell":
self.bell()
diff --git a/docs/guide/events.md b/docs/guide/events.md
index 1e6136c8f..0629e17e5 100644
--- a/docs/guide/events.md
+++ b/docs/guide/events.md
@@ -181,6 +181,8 @@ In the following example we have three buttons, each of which does something dif
--8<-- "docs/examples/events/on_decorator01.py"
```
+ 1. The message handler is called when any button is pressed
+
=== "Output"
```{.textual path="docs/examples/events/on_decorator01.py"}
diff --git a/pyproject.toml b/pyproject.toml
index e1b4cbee6..ba155e993 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "textual"
-version = "0.22.3"
+version = "0.23.0"
homepage = "https://github.com/Textualize/textual"
description = "Modern Text User Interface framework"
authors = ["Will McGugan "]