diff --git a/include/modules/dwl/layout.hpp b/include/modules/dwl/layout.hpp
new file mode 100644
index 000000000..a17ed7354
--- /dev/null
+++ b/include/modules/dwl/layout.hpp
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <fmt/format.h>
+
+#include <string>
+
+#include "AAppIconLabel.hpp"
+#include "bar.hpp"
+#include "dwl-ipc-unstable-v2-client-protocol.h"
+#include "util/json.hpp"
+
+namespace waybar::modules::dwl {
+
+class Layout : public ALabel, public sigc::trackable {
+ public:
+  Layout(const std::string &, const waybar::Bar &, const Json::Value &);
+  ~Layout();
+
+  void handle_layout(const uint32_t layout);
+  void handle_layout_symbol(const char *layout_symbol);
+  void handle_frame();
+
+  struct zdwl_ipc_manager_v2 *status_manager_;
+
+ private:
+  const Bar &bar_;
+
+  std::string layout_symbol_;
+  uint32_t layout_;
+
+  struct zdwl_ipc_output_v2 *output_status_;
+};
+
+}  // namespace waybar::modules::dwl
diff --git a/include/modules/dwl/window.hpp b/include/modules/dwl/window.hpp
index 435863999..99c297c17 100644
--- a/include/modules/dwl/window.hpp
+++ b/include/modules/dwl/window.hpp
@@ -16,10 +16,8 @@ class Window : public AAppIconLabel, public sigc::trackable {
   Window(const std::string &, const waybar::Bar &, const Json::Value &);
   ~Window();
 
-  void handle_layout(const uint32_t layout);
   void handle_title(const char *title);
   void handle_appid(const char *ppid);
-  void handle_layout_symbol(const char *layout_symbol);
   void handle_frame();
 
   struct zdwl_ipc_manager_v2 *status_manager_;
@@ -29,8 +27,6 @@ class Window : public AAppIconLabel, public sigc::trackable {
 
   std::string title_;
   std::string appid_;
-  std::string layout_symbol_;
-  uint32_t layout_;
 
   struct zdwl_ipc_output_v2 *output_status_;
 };
diff --git a/man/waybar-dwl-layout.5.scd b/man/waybar-dwl-layout.5.scd
new file mode 100644
index 000000000..e18982cb6
--- /dev/null
+++ b/man/waybar-dwl-layout.5.scd
@@ -0,0 +1,79 @@
+waybar-dwl-layout(5)
+
+# NAME
+
+waybar - dwl layout module
+
+# DESCRIPTION
+
+The *layout* module displays the layout of the currently focused workspace in DWL
+
+# CONFIGURATION
+
+Addressed by *dwl/layout*
+
+*format*: ++
+	typeof: string ++
+	default: {layout} ++
+	The format, how information should be displayed.
+
+*rotate*: ++
+	typeof: integer ++
+	Positive value to rotate the text label (in 90 degree increments).
+
+*max-length*: ++
+	typeof: integer ++
+	The maximum length in character the module should display.
+
+*min-length*: ++
+	typeof: integer ++
+	The minimum length in characters the module should accept.
+
+*align*: ++
+	typeof: float ++
+	The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text.
+
+*justify*: ++
+	typeof: string ++
+	The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning.
+
+*on-click*: ++
+	typeof: string ++
+	Command to execute when clicked on the module.
+
+*on-click-middle*: ++
+	typeof: string ++
+	Command to execute when middle-clicked on the module using mousewheel.
+
+*on-click-right*: ++
+	typeof: string ++
+	Command to execute when you right-click on the module.
+
+*on-update*: ++
+	typeof: string ++
+	Command to execute when the module is updated.
+
+*on-scroll-up*: ++
+	typeof: string ++
+	Command to execute when scrolling up on the module.
+
+*on-scroll-down*: ++
+	typeof: string ++
+	Command to execute when scrolling down on the module.
+
+*smooth-scrolling-threshold*: ++
+	typeof: double ++
+	Threshold to be used when scrolling.
+
+# FORMAT REPLACEMENTS
+
+*{layout}*: The layout of the focused window.
+
+# EXAMPLES
+
+```
+"dwl/layout": {
+	"format": "{layout}",
+	"max-length": 50,
+}
+```
diff --git a/man/waybar-dwl-window.5.scd b/man/waybar-dwl-window.5.scd
index 9ac33d948..01f077194 100644
--- a/man/waybar-dwl-window.5.scd
+++ b/man/waybar-dwl-window.5.scd
@@ -95,8 +95,6 @@ Addressed by *dwl/window*
 
 *{app_id}*: The app_id of the focused window.
 
-*{layout}*: The layout of the focused window.
-
 # REWRITE RULES
 
 *rewrite* is an object where keys are regular expressions and values are
diff --git a/meson.build b/meson.build
index 726d492bb..d11d707ae 100644
--- a/meson.build
+++ b/meson.build
@@ -295,6 +295,7 @@ if true
     add_project_arguments('-DHAVE_DWL', language: 'cpp')
     src_files += files('src/modules/dwl/tags.cpp')
     src_files += files('src/modules/dwl/window.cpp')
+    src_files += files('src/modules/dwl/layout.cpp')
     man_files += files('man/waybar-dwl-tags.5.scd')
     man_files += files('man/waybar-dwl-window.5.scd')
 endif
diff --git a/src/factory.cpp b/src/factory.cpp
index 6c2313e38..be664c8f0 100644
--- a/src/factory.cpp
+++ b/src/factory.cpp
@@ -29,6 +29,7 @@
 #ifdef HAVE_DWL
 #include "modules/dwl/tags.hpp"
 #include "modules/dwl/window.hpp"
+#include "modules/dwl/layout.hpp"
 #endif
 #ifdef HAVE_HYPRLAND
 #include "modules/hyprland/language.hpp"
@@ -196,6 +197,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name,
     if (ref == "dwl/window") {
       return new waybar::modules::dwl::Window(id, bar_, config_[name]);
     }
+    if (ref == "dwl/layout") {
+      return new waybar::modules::dwl::Layout(id, bar_, config_[name]);
+    }
 #endif
 #ifdef HAVE_HYPRLAND
     if (ref == "hyprland/window") {
diff --git a/src/modules/dwl/layout.cpp b/src/modules/dwl/layout.cpp
new file mode 100644
index 000000000..fbbf95d13
--- /dev/null
+++ b/src/modules/dwl/layout.cpp
@@ -0,0 +1,106 @@
+#include "modules/dwl/layout.hpp"
+
+#include <spdlog/spdlog.h>
+
+#include "client.hpp"
+#include "dwl-ipc-unstable-v2-client-protocol.h"
+#include "glibmm/markup.h"
+#include "util/rewrite_string.hpp"
+
+namespace waybar::modules::dwl {
+
+static void toggle_visibility(void *data, zdwl_ipc_output_v2 *zdwl_output_v2) {
+  // Intentionally empty
+}
+
+static void active(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t active) {
+  // Intentionally empty
+}
+
+static void set_tag(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t tag, uint32_t state,
+                    uint32_t clients, uint32_t focused) {
+  // Intentionally empty
+}
+
+static void set_layout_symbol(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *layout) {
+  static_cast<Layout *>(data)->handle_layout_symbol(layout);
+}
+
+static void title(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *title) {
+  // Intentionally empty
+}
+
+static void dwl_frame(void *data, zdwl_ipc_output_v2 *zdwl_output_v2) {
+  static_cast<Layout *>(data)->handle_frame();
+}
+
+static void set_layout(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t layout) {
+  static_cast<Layout *>(data)->handle_layout(layout);
+}
+
+static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid) {
+  // Intentionally empty
+};
+
+static const zdwl_ipc_output_v2_listener output_status_listener_impl{
+    .toggle_visibility = toggle_visibility,
+    .active = active,
+    .tag = set_tag,
+    .layout = set_layout,
+    .title = title,
+    .appid = appid,
+    .layout_symbol = set_layout_symbol,
+    .frame = dwl_frame,
+};
+
+static void handle_global(void *data, struct wl_registry *registry, uint32_t name,
+                          const char *interface, uint32_t version) {
+  if (std::strcmp(interface, zdwl_ipc_manager_v2_interface.name) == 0) {
+    static_cast<Layout *>(data)->status_manager_ = static_cast<struct zdwl_ipc_manager_v2 *>(
+        (zdwl_ipc_manager_v2 *)wl_registry_bind(registry, name, &zdwl_ipc_manager_v2_interface, 1));
+  }
+}
+
+static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) {
+  /* Ignore event */
+}
+
+static const wl_registry_listener registry_listener_impl = {.global = handle_global,
+                                                            .global_remove = handle_global_remove};
+
+Layout::Layout(const std::string &id, const Bar &bar, const Json::Value &config)
+    : waybar::ALabel(config, "layout", id, "{layout}", false, false, false, false), bar_(bar) {
+  struct wl_display *display = Client::inst()->wl_display;
+  struct wl_registry *registry = wl_display_get_registry(display);
+
+  wl_registry_add_listener(registry, &registry_listener_impl, this);
+  wl_display_roundtrip(display);
+
+  if (status_manager_ == nullptr) {
+    spdlog::error("dwl_status_manager_v2 not advertised");
+    return;
+  }
+
+  struct wl_output *output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
+  output_status_ = zdwl_ipc_manager_v2_get_output(status_manager_, output);
+  zdwl_ipc_output_v2_add_listener(output_status_, &output_status_listener_impl, this);
+  zdwl_ipc_manager_v2_destroy(status_manager_);
+}
+
+Layout::~Layout() {
+  if (output_status_ != nullptr) {
+    zdwl_ipc_output_v2_destroy(output_status_);
+  }
+}
+
+void Layout::handle_layout_symbol(const char *layout_symbol) {
+  layout_symbol_ = Glib::Markup::escape_text(layout_symbol);
+}
+
+void Layout::handle_layout(const uint32_t layout) { layout_ = layout; }
+
+void Layout::handle_frame() {
+  label_.set_markup(fmt::format(fmt::runtime(format_), fmt::arg("layout", layout_symbol_)));
+}
+
+}  // namespace waybar::modules::dwl
diff --git a/src/modules/dwl/window.cpp b/src/modules/dwl/window.cpp
index a960a1f04..92cc301b1 100644
--- a/src/modules/dwl/window.cpp
+++ b/src/modules/dwl/window.cpp
@@ -28,7 +28,7 @@ static void set_tag(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t tag
 }
 
 static void set_layout_symbol(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *layout) {
-  static_cast<Window *>(data)->handle_layout_symbol(layout);
+  // Intentionally empty
 }
 
 static void title(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *title) {
@@ -40,7 +40,7 @@ static void dwl_frame(void *data, zdwl_ipc_output_v2 *zdwl_output_v2) {
 }
 
 static void set_layout(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t layout) {
-  static_cast<Window *>(data)->handle_layout(layout);
+  // Intentionally empty
 }
 
 static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid) {
@@ -102,16 +102,9 @@ void Window::handle_title(const char *title) { title_ = Glib::Markup::escape_tex
 
 void Window::handle_appid(const char *appid) { appid_ = Glib::Markup::escape_text(appid); }
 
-void Window::handle_layout_symbol(const char *layout_symbol) {
-  layout_symbol_ = Glib::Markup::escape_text(layout_symbol);
-}
-
-void Window::handle_layout(const uint32_t layout) { layout_ = layout; }
-
 void Window::handle_frame() {
   label_.set_markup(waybar::util::rewriteString(
-      fmt::format(fmt::runtime(format_), fmt::arg("title", title_),
-                  fmt::arg("layout", layout_symbol_), fmt::arg("app_id", appid_)),
+      fmt::format(fmt::runtime(format_), fmt::arg("title", title_), fmt::arg("app_id", appid_)),
       config_["rewrite"]));
   updateAppIconName(appid_, "");
   updateAppIcon();