diff --git a/CHANGELOG.md b/CHANGELOG.md
index 69b4df9..26395e7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,8 @@
   [#396](https://github.com/lambda-fairy/maud/pull/396)
 - Support `axum` v0.7 through `axum-core` v0.4 and `http` v1
   [#401](https://github.com/lambda-fairy/maud/pull/401)
+- Add support for `warp` v0.3.6
+  [#404](https://github.com/lambda-fairy/maud/pull/404)
 - Support `rocket` v0.5
   [#406](https://github.com/lambda-fairy/maud/pull/406)
 
diff --git a/docs/content/web-frameworks.md b/docs/content/web-frameworks.md
index b5442a5..3d3be04 100644
--- a/docs/content/web-frameworks.md
+++ b/docs/content/web-frameworks.md
@@ -7,6 +7,7 @@ Maud includes support for these web frameworks: [Actix], [Rocket], [Rouille], [T
 [Rouille]: https://github.com/tomaka/rouille
 [Tide]: https://docs.rs/tide/
 [Axum]: https://docs.rs/axum/
+[Warp]: https://seanmonstar.com/blog/warp/
 
 # Actix
 
@@ -171,3 +172,28 @@ async fn main() {
     axum::serve(listener, app.into_make_service()).await.unwrap();
 }
 ```
+
+# Warp
+
+Warp support is available with the "warp" feature:
+
+```toml
+# ...
+[dependencies]
+maud = { version = "*", features = ["warp"] }
+# ...
+```
+
+This enables `Markup` to be of type `warp::Reply`, making it possible to return it
+immediately from a handler.
+
+```rust,no_run
+use maud::html;
+use warp::Filter;
+
+#[tokio::main]
+async fn main() {
+    let hello = warp::any().map(|| html! { h1 { "Hello, world!" } });
+    warp::serve(hello).run(([127, 0, 0, 1], 8000)).await;
+}
+```
diff --git a/doctest/Cargo.toml b/doctest/Cargo.toml
index 9f47943..f5fe7a5 100644
--- a/doctest/Cargo.toml
+++ b/doctest/Cargo.toml
@@ -7,13 +7,14 @@ edition = "2021"
 [dependencies]
 actix-web = { version = "4.0.0-rc.2", default-features = false, features = ["macros"] }
 ammonia = "3"
-maud = { path = "../maud", features = ["actix-web", "rocket", "tide", "axum"] }
+maud = { path = "../maud", features = ["actix-web", "rocket", "tide", "axum", "warp"] }
 pulldown-cmark = "0.8"
 rocket = "0.5"
 rouille = "3"
 tide = "0.16"
 tokio = { version = "1.9.0", features = ["rt", "macros", "rt-multi-thread"] }
 axum = "0.7"
+warp = "0.3.6"
 
 [dependencies.async-std]
 version = "1.9.0"
diff --git a/maud/Cargo.toml b/maud/Cargo.toml
index d4b61ca..62e2055 100644
--- a/maud/Cargo.toml
+++ b/maud/Cargo.toml
@@ -27,6 +27,7 @@ actix-web-dep = { package = "actix-web", version = "4", optional = true, default
 tide = { version = "0.16.0", optional = true, default-features = false }
 axum-core = { version = "0.4", optional = true }
 http = { version = "1", optional = true }
+warp = { version = "0.3.6", optional = true }
 
 [dev-dependencies]
 trybuild = { version = "1.0.33", features = ["diff"] }
diff --git a/maud/src/lib.rs b/maud/src/lib.rs
index d6b45b7..70daa93 100644
--- a/maud/src/lib.rs
+++ b/maud/src/lib.rs
@@ -353,6 +353,19 @@ mod axum_support {
     }
 }
 
+#[cfg(feature = "warp")]
+mod warp_support {
+    use crate::PreEscaped;
+    use alloc::string::String;
+    use warp::reply::{self, Reply, Response};
+
+    impl Reply for PreEscaped<String> {
+        fn into_response(self) -> Response {
+            reply::html(self.into_string()).into_response()
+        }
+    }
+}
+
 #[doc(hidden)]
 pub mod macro_private {
     use crate::{display, Render};