diff --git a/src/action.rs b/src/action.rs
index bfee1483..13529016 100644
--- a/src/action.rs
+++ b/src/action.rs
@@ -6,6 +6,7 @@ use std::{
 
 use floem_reactive::Scope;
 use kurbo::{Point, Size, Vec2};
+use winit::window::ResizeDirection;
 
 use crate::{
     app::{add_app_update_event, AppUpdateEvent},
@@ -39,6 +40,10 @@ pub fn drag_window() {
     add_update_message(UpdateMessage::DragWindow);
 }
 
+pub fn drag_resize_window(direction: ResizeDirection) {
+    add_update_message(UpdateMessage::DragResizeWindow(direction));
+}
+
 pub fn set_window_delta(delta: Vec2) {
     add_update_message(UpdateMessage::SetWindowDelta(delta));
 }
diff --git a/src/app_handle.rs b/src/app_handle.rs
index c8f30a0b..69bca02b 100644
--- a/src/app_handle.rs
+++ b/src/app_handle.rs
@@ -150,7 +150,9 @@ impl ApplicationHandle {
             WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
                 window_handle.scale(scale_factor);
             }
-            WindowEvent::ThemeChanged(_) => {}
+            WindowEvent::ThemeChanged(theme) => {
+                window_handle.theme_changed(theme);
+            }
             WindowEvent::Occluded(_) => {}
             WindowEvent::MenuAction(id) => {
                 window_handle.menu_action(id);
diff --git a/src/event.rs b/src/event.rs
index 97e71df1..a39b4326 100644
--- a/src/event.rs
+++ b/src/event.rs
@@ -1,5 +1,5 @@
 use kurbo::{Point, Size};
-use winit::keyboard::KeyCode;
+use winit::{keyboard::KeyCode, window::Theme};
 
 use crate::{
     keyboard::KeyEvent,
@@ -31,6 +31,7 @@ pub enum EventListener {
     PointerWheel,
     FocusGained,
     FocusLost,
+    ThemeChanged,
     WindowClosed,
     WindowResized,
     WindowMoved,
@@ -60,6 +61,7 @@ pub enum Event {
     WindowResized(Size),
     WindowMoved(Point),
     WindowMaximizeChanged(bool),
+    ThemeChanged(Theme),
     FocusGained,
     FocusLost,
 }
@@ -77,6 +79,7 @@ impl Event {
             | Event::ImeDisabled
             | Event::ImePreedit { .. }
             | Event::ImeCommit(_)
+            | Event::ThemeChanged(_)
             | Event::WindowClosed
             | Event::WindowResized(_)
             | Event::WindowMoved(_)
@@ -101,6 +104,7 @@ impl Event {
             | Event::ImeDisabled
             | Event::ImePreedit { .. }
             | Event::ImeCommit(_)
+            | Event::ThemeChanged(_)
             | Event::WindowClosed
             | Event::WindowResized(_)
             | Event::WindowMoved(_)
@@ -137,6 +141,7 @@ impl Event {
             | Event::KeyDown(_)
             | Event::KeyUp(_) => false,
             Event::PointerMove(_)
+            | Event::ThemeChanged(_)
             | Event::WindowClosed
             | Event::WindowResized(_)
             | Event::WindowMoved(_)
@@ -160,6 +165,7 @@ impl Event {
             | Event::ImeEnabled
             | Event::ImeDisabled
             | Event::ImePreedit { .. }
+            | Event::ThemeChanged(_)
             | Event::ImeCommit(_)
             | Event::WindowClosed
             | Event::WindowResized(_)
@@ -191,6 +197,7 @@ impl Event {
             | Event::ImeEnabled
             | Event::ImeDisabled
             | Event::ImePreedit { .. }
+            | Event::ThemeChanged(_)
             | Event::ImeCommit(_)
             | Event::WindowClosed
             | Event::WindowResized(_)
@@ -220,6 +227,7 @@ impl Event {
             | Event::ImeEnabled
             | Event::ImeDisabled
             | Event::ImePreedit { .. }
+            | Event::ThemeChanged(_)
             | Event::ImeCommit(_)
             | Event::WindowClosed
             | Event::WindowResized(_)
@@ -251,6 +259,7 @@ impl Event {
             Event::WindowLostFocus => Some(EventListener::WindowLostFocus),
             Event::FocusLost => Some(EventListener::FocusLost),
             Event::FocusGained => Some(EventListener::FocusGained),
+            Event::ThemeChanged(_) => Some(EventListener::ThemeChanged),
         }
     }
 }
diff --git a/src/style.rs b/src/style.rs
index e80d01b6..431c97c5 100644
--- a/src/style.rs
+++ b/src/style.rs
@@ -60,6 +60,14 @@ pub enum CursorStyle {
     Text,
     ColResize,
     RowResize,
+    WResize,
+    EResize,
+    SResize,
+    NResize,
+    NwResize,
+    NeResize,
+    SwResize,
+    SeResize,
 }
 
 #[derive(Debug, Clone, Copy)]
diff --git a/src/update.rs b/src/update.rs
index a414b976..4d024e0e 100644
--- a/src/update.rs
+++ b/src/update.rs
@@ -1,6 +1,7 @@
 use std::{any::Any, cell::RefCell, collections::HashMap};
 
 use kurbo::{Point, Size, Vec2};
+use winit::window::ResizeDirection;
 
 use crate::{
     animate::{AnimUpdateMsg, Animation},
@@ -89,6 +90,7 @@ pub(crate) enum UpdateMessage {
     SetWindowMaximized(bool),
     MinimizeWindow,
     DragWindow,
+    DragResizeWindow(ResizeDirection),
     SetWindowDelta(Vec2),
     Animation {
         id: Id,
diff --git a/src/views/drag_resize_window_area.rs b/src/views/drag_resize_window_area.rs
new file mode 100644
index 00000000..64773643
--- /dev/null
+++ b/src/views/drag_resize_window_area.rs
@@ -0,0 +1,105 @@
+use kurbo::Rect;
+use winit::window::ResizeDirection;
+
+use crate::{
+    action::drag_resize_window,
+    event::{Event, EventListener},
+    id::Id,
+    style::CursorStyle,
+    view::View,
+};
+
+use super::Decorators;
+
+pub struct DragResizeWindowArea<V: View> {
+    id: Id,
+    child: V,
+}
+
+pub fn drag_resize_window_area<V: View>(
+    direction: ResizeDirection,
+    child: V,
+) -> DragResizeWindowArea<V> {
+    let id = Id::next();
+    DragResizeWindowArea { id, child }
+        .on_event(EventListener::PointerDown, move |_| {
+            drag_resize_window(direction);
+            true
+        })
+        .style(move |s| {
+            let cursor = match direction {
+                ResizeDirection::East => CursorStyle::EResize,
+                ResizeDirection::North => CursorStyle::NResize,
+                ResizeDirection::NorthEast => CursorStyle::NeResize,
+                ResizeDirection::NorthWest => CursorStyle::NwResize,
+                ResizeDirection::South => CursorStyle::SResize,
+                ResizeDirection::SouthEast => CursorStyle::SeResize,
+                ResizeDirection::SouthWest => CursorStyle::SwResize,
+                ResizeDirection::West => CursorStyle::WResize,
+            };
+            s.cursor(cursor)
+        })
+}
+
+impl<V: View> View for DragResizeWindowArea<V> {
+    fn id(&self) -> Id {
+        self.id
+    }
+
+    fn child(&self, id: Id) -> Option<&dyn View> {
+        if self.child.id() == id {
+            Some(&self.child)
+        } else {
+            None
+        }
+    }
+
+    fn child_mut(&mut self, id: Id) -> Option<&mut dyn View> {
+        if self.child.id() == id {
+            Some(&mut self.child)
+        } else {
+            None
+        }
+    }
+
+    fn children(&self) -> Vec<&dyn View> {
+        vec![&self.child]
+    }
+
+    fn children_mut(&mut self) -> Vec<&mut dyn View> {
+        vec![&mut self.child]
+    }
+
+    fn update(
+        &mut self,
+        _cx: &mut crate::context::UpdateCx,
+        _state: Box<dyn std::any::Any>,
+    ) -> crate::view::ChangeFlags {
+        crate::view::ChangeFlags::empty()
+    }
+
+    fn layout(&mut self, cx: &mut crate::context::LayoutCx) -> taffy::prelude::Node {
+        cx.layout_node(self.id, true, |cx| vec![self.child.layout_main(cx)])
+    }
+
+    fn compute_layout(&mut self, cx: &mut crate::context::LayoutCx) -> Option<Rect> {
+        Some(self.child.compute_layout_main(cx))
+    }
+
+    fn event(
+        &mut self,
+        cx: &mut crate::context::EventCx,
+        id_path: Option<&[Id]>,
+        event: Event,
+    ) -> bool {
+        if cx.should_send(self.child.id(), &event) {
+            self.child.event_main(cx, id_path, event)
+        } else {
+            false
+        }
+    }
+
+    fn paint(&mut self, cx: &mut crate::context::PaintCx) {
+        self.child.paint_main(cx);
+    }
+}
diff --git a/src/views/mod.rs b/src/views/mod.rs
index 5f940044..7926baf3 100644
--- a/src/views/mod.rs
+++ b/src/views/mod.rs
@@ -50,3 +50,6 @@ pub use empty::*;
 
 mod drag_window_area;
 pub use drag_window_area::*;
+
+mod drag_resize_window_area;
+pub use drag_resize_window_area::*;
diff --git a/src/window.rs b/src/window.rs
index ed611778..05a1c838 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -1,4 +1,6 @@
 use kurbo::{Point, Size};
+pub use winit::window::ResizeDirection;
+pub use winit::window::Theme;
 pub use winit::window::WindowId;
 
 use crate::{
diff --git a/src/window_handle.rs b/src/window_handle.rs
index 28e2dd55..34c6702b 100644
--- a/src/window_handle.rs
+++ b/src/window_handle.rs
@@ -10,7 +10,7 @@ use winit::{
     dpi::{LogicalPosition, LogicalSize},
     event::{ElementState, Ime, MouseButton, MouseScrollDelta},
     keyboard::{Key, ModifiersState},
-    window::CursorIcon,
+    window::{CursorIcon, Theme},
 };
 
 #[cfg(target_os = "linux")]
@@ -49,6 +49,7 @@ pub(crate) struct WindowHandle {
     app_state: AppState,
     paint_state: PaintState,
     size: RwSignal<Size>,
+    theme: RwSignal<Option<Theme>>,
     is_maximized: bool,
     pub(crate) scale: f64,
     pub(crate) modifiers: ModifiersState,
@@ -70,6 +71,7 @@ impl WindowHandle {
         let size: LogicalSize<f64> = window.inner_size().to_logical(scale);
         let size = Size::new(size.width, size.height);
         let size = scope.create_rw_signal(Size::new(size.width, size.height));
+        let theme = scope.create_rw_signal(window.theme());
         let is_maximized = window.is_maximized();
 
         #[cfg(target_os = "linux")]
@@ -104,6 +106,7 @@ impl WindowHandle {
             app_state: AppState::new(),
             paint_state,
             size,
+            theme,
             is_maximized,
             scale,
             modifiers: ModifiersState::default(),
@@ -269,6 +272,10 @@ impl WindowHandle {
         self.request_paint();
     }
 
+    pub(crate) fn theme_changed(&mut self, theme: Theme) {
+        self.theme.set(Some(theme));
+    }
+
     pub(crate) fn size(&mut self, size: Size) {
         self.size.set(size);
         self.app_state.update_screen_size_bp(size);
@@ -599,6 +606,11 @@ impl WindowHandle {
                             let _ = window.drag_window();
                         }
                     }
+                    UpdateMessage::DragResizeWindow(direction) => {
+                        if let Some(window) = self.window.as_ref() {
+                            let _ = window.drag_resize_window(direction);
+                        }
+                    }
                     UpdateMessage::ToggleWindowMaximized => {
                         if let Some(window) = self.window.as_ref() {
                             window.set_maximized(!window.is_maximized());
@@ -880,6 +892,14 @@ impl WindowHandle {
             Some(CursorStyle::Text) => CursorIcon::Text,
             Some(CursorStyle::ColResize) => CursorIcon::ColResize,
             Some(CursorStyle::RowResize) => CursorIcon::RowResize,
+            Some(CursorStyle::WResize) => CursorIcon::WResize,
+            Some(CursorStyle::EResize) => CursorIcon::EResize,
+            Some(CursorStyle::NwResize) => CursorIcon::NwResize,
+            Some(CursorStyle::NeResize) => CursorIcon::NeResize,
+            Some(CursorStyle::SwResize) => CursorIcon::SwResize,
+            Some(CursorStyle::SeResize) => CursorIcon::SeResize,
+            Some(CursorStyle::SResize) => CursorIcon::SResize,
+            Some(CursorStyle::NResize) => CursorIcon::NResize,
             None => CursorIcon::Default,
         };
         if cursor != self.app_state.last_cursor {