Resizable

The resizable component system provides a flexible way to create layouts with resizable panels. It supports both horizontal and vertical resizing, nested layouts, size constraints, and drag handles. Perfect for creating paned interfaces, split views, and adjustable dashboards.

Install

#Import

use gpui_kit::component::resizable::{
    h_resizable, v_resizable, resizable_panel,
    ResizablePanelGroup, ResizablePanel, ResizableState, ResizablePanelEvent
};
// Installed in ui/components. That directory is on the Slint include path, so import by file name:
import { ResizablePane, ResizeHandle } from "resizable.slint";
use iced::widget::{column, container, mouse_area, row, scrollable, svg, text};
use iced::{Alignment, Element, Font, Length, Task, Theme, font};

#Usage

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// Two panes around one handle. The fixed pane reads the handle's split, so
// dragging the divider moves exactly one edge.
let usage = row![
    ResizablePane::new()
        .width(app.usage)
        .child(pane("Sidebar", "Drag the divider.")),
    handle(Slot::Usage, app.usage).minimum(80.0),
    ResizablePane::new().child(pane("Content", "Takes whatever is left.")),
]
.width(Length::Fill)
.height(160.0);

Use h_resizable to create a horizontal layout, v_resizable to create a vertical layout.

The first argument is the id for this [ResizablePanelGroup].

h_resizable("my-layout")
    .on_resize(|state, window, cx| {
        // Handle resize event
        // You can read the panel sizes from the state.
        let state = state.read(cx);
        let sizes = state.sizes();
    })
    .child(
        // Use resizable_panel() to create a sized panel.
        resizable_panel()
            .size(px(200.))
            .child("Left Panel")
    )
    .child(
        // Or you can just add AnyElement without a size.
        div()
            .child("Right Panel")
            .into_any_element()
    )
HorizontalLayout {
    height: 160px;
    spacing: 0;
    ResizablePane {
        width: u1.split;
        VerticalLayout {
            padding: 12px;
            spacing: 4px;
            Text {
                text: "Sidebar";
                color: Theme.foreground;
                font-size: Theme.font-size-sm;
                font-weight: 600;
            }
            Text {
                text: "Drag the divider.";
                color: Theme.muted-foreground;
                font-size: Theme.font-size-xs;
            }
        }
    }
    u1 := ResizeHandle {}
    ResizablePane {
        horizontal-stretch: 1;
        VerticalLayout {
            padding: 12px;
            spacing: 4px;
            Text {
                text: "Content";
                color: Theme.foreground;
                font-size: Theme.font-size-sm;
                font-weight: 600;
            }
            Text {
                text: "Takes whatever is left.";
                color: Theme.muted-foreground;
                font-size: Theme.font-size-xs;
            }
        }
    }
}

The v_resizable component is used to create a vertical layout.

v_resizable("vertical-layout")
    .child(
        resizable_panel()
            .size(px(100.))
            .child("Top Panel")
    )
    .child(
        div()
            .child("Bottom Panel")
            .into_any_element()
    )
SlintNo Slint version of this example yet.

#Resize Handle Appearance

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// The divider is a 6px strip in the border colour, and its target reaches
// four pixels into each pane. Hovering or holding it turns the strip the
// primary colour at 45%, the one state it draws.
let appearance = column![
    row![
        ResizablePane::new()
            .width(app.appearance)
            .child(pane("Pane", "A hairline, easy to grab.")),
        handle(Slot::Appearance, app.appearance).minimum(120.0),
        ResizablePane::new().child(pane("Pane", "The strip never widens.")),
    ]
    .width(Length::Fill)
    .height(120.0),
    muted_line(
        "The strip is 6px of the border colour; its hit box reaches 4px into each pane.",
    ),
]
.spacing(8.0);

A divider rests as a hairline. As the pointer engages it, an indicator grows and solidifies on top of that line through three levels — hovered, pressed, dragging — so a drag stays readable after the pointer has left the handle’s own band, which happens within a pixel or two of the drag starting.

h_resizable, v_resizable and the Dock install this appearance. resize_handle_appearance() is exported for a handle you build yourself, or to pass explicitly to with_handle_appearance:

gpui_kit::base::h_resizable("my-layout")
    .with_handle_appearance(resize_handle_appearance())
VerticalLayout {
    spacing: 8px;
    alignment: start;
    Text {
        text: "The handle is a 6px strip in the border colour. It widens its own target by 4px on each side, so the divider is easy to grab without drawing a thicker line.";
        color: Theme.muted-foreground;
        font-size: Theme.font-size-sm;
        wrap: word-wrap;
    }
}

The indicator’s duration and easing come from the theme’s motion tokens, and a system reduced-motion preference takes it straight to its target.

#Panel Size Constraints

resizable_panel()
    .size(px(200.))                    // Initial size
    .size_range(px(150.)..px(400.))    // Min and max size
    .child("Constrained Panel")
HorizontalLayout {
    height: 160px;
    spacing: 0;
    ResizablePane {
        width: u2.split;
        VerticalLayout {
            padding: 12px;
            spacing: 4px;
            Text {
                text: "240px to start";
                color: Theme.foreground;
                font-size: Theme.font-size-sm;
            }
            Text {
                text: "Stops at 120px.";
                color: Theme.muted-foreground;
                font-size: Theme.font-size-xs;
            }
        }
    }
    u2 := ResizeHandle {
        split: 240px;
        minimum: 120px;
    }
    ResizablePane {
        horizontal-stretch: 1;
        VerticalLayout {
            padding: 12px;
            Text {
                text: "Content";
                color: Theme.foreground;
                font-size: Theme.font-size-sm;
            }
        }
    }
}
// `minimum` is where the divider stops, so the pane cannot be dragged away.
let constraints = row![
    ResizablePane::new()
        .width(app.constraints)
        .child(pane("240px to start", "Stops at 120px.")),
    handle(Slot::Constraints, app.constraints).minimum(120.0),
    ResizablePane::new().child(pane("Content", "")),
]
.width(Length::Fill)
.height(160.0);

#Multiple Panels

h_resizable("multi-panel", state)
    .child(
        resizable_panel()
            .size(px(200.))
            .size_range(px(150.)..px(300.))
            .child("Left Panel")
    )
    .child(
        resizable_panel()
            .child("Center Panel")
    )
    .child(
        resizable_panel()
            .size(px(250.))
            .child("Right Panel")
    )
HorizontalLayout {
    height: 160px;
    spacing: 0;
    ResizablePane {
        width: m1.split;
        VerticalLayout {
            padding: 12px;
            Text {
                text: "Explorer";
                color: Theme.foreground;
                font-size: Theme.font-size-sm;
                font-weight: 600;
            }
        }
    }
    m1 := ResizeHandle {
        split: 180px;
        minimum: 120px;
    }
    ResizablePane {
        width: m2.split;
        VerticalLayout {
            padding: 12px;
            Text {
                text: "Outline";
                color: Theme.foreground;
                font-size: Theme.font-size-sm;
                font-weight: 600;
            }
        }
    }
    m2 := ResizeHandle {
        split: 180px;
        minimum: 120px;
    }
    ResizablePane {
        horizontal-stretch: 1;
        VerticalLayout {
            padding: 12px;
            Text {
                text: "Editor";
                color: Theme.foreground;
                font-size: Theme.font-size-sm;
                font-weight: 600;
            }
        }
    }
}
// Three panes are two handles in a row, each owning its own edge.
let multiple = row![
    ResizablePane::new()
        .width(app.multi_a)
        .child(pane("Explorer", "")),
    handle(Slot::MultiA, app.multi_a).minimum(120.0),
    ResizablePane::new()
        .width(app.multi_b)
        .child(pane("Outline", "")),
    handle(Slot::MultiB, app.multi_b).minimum(120.0),
    ResizablePane::new().child(pane("Editor", "")),
]
.width(Length::Fill)
.height(160.0);

#Nested Layouts

v_resizable("main-layout", window, cx)
    .child(
        resizable_panel()
            .size(px(300.))
            .child(
                h_resizable("nested-layout", window, cx)
                    .child(
                        resizable_panel()
                            .size(px(200.))
                            .child("Top Left")
                    )
                    .child(
                        resizable_panel()
                            .child("Top Right")
                    )
            )
    )
    .child(
        resizable_panel()
            .child("Bottom Panel")
    )
HorizontalLayout {
    height: 200px;
    spacing: 0;
    ResizablePane {
        width: n1.split;
        VerticalLayout {
            padding: 12px;
            Text {
                text: "Explorer";
                color: Theme.foreground;
                font-size: Theme.font-size-sm;
                font-weight: 600;
            }
        }
    }
    n1 := ResizeHandle {
        split: 160px;
        minimum: 80px;
    }
    ResizablePane {
        horizontal-stretch: 1;
        HorizontalLayout {
            spacing: 0;
            ResizablePane {
                width: n2.split;
                VerticalLayout {
                    padding: 12px;
                    Text {
                        text: "Outline";
                        color: Theme.foreground;
                        font-size: Theme.font-size-sm;
                        font-weight: 600;
                    }
                }
            }
            n2 := ResizeHandle {
                split: 160px;
                minimum: 80px;
            }
            ResizablePane {
                horizontal-stretch: 1;
                VerticalLayout {
                    padding: 12px;
                    Text {
                        text: "Editor";
                        color: Theme.foreground;
                        font-size: Theme.font-size-sm;
                        font-weight: 600;
                    }
                }
            }
        }
    }
}
// A split inside a pane is a normal element, so a workspace nests freely.
let nested = row![
    ResizablePane::new()
        .width(app.nested_outer)
        .child(pane("Explorer", "")),
    handle(Slot::NestedOuter, app.nested_outer).minimum(80.0),
    ResizablePane::new().child(
        row![
            ResizablePane::new()
                .width(app.nested_inner)
                .child(pane("Outline", "")),
            handle(Slot::NestedInner, app.nested_inner).minimum(80.0),
            ResizablePane::new().child(pane("Editor", "")),
        ]
        .width(Length::Fill)
        .height(Length::Fill),
    ),
]
.width(Length::Fill)
.height(200.0);

#Nested Panel Groups

h_resizable("outer", window, cx)
    .child(
        resizable_panel()
            .size(px(200.))
            .child("Left Panel")
    )
    .group(
        v_resizable("inner", window, cx)
            .child(
                resizable_panel()
                    .size(px(150.))
                    .child("Top Right")
            )
            .child(
                resizable_panel()
                    .child("Bottom Right")
            )
    )
VerticalLayout {
    height: 240px;
    spacing: 0;
    ResizablePane {
        vertical-stretch: 1;
        HorizontalLayout {
            spacing: 0;
            ResizablePane {
                width: g1.split;
                VerticalLayout {
                    padding: 12px;
                    Text {
                        text: "Left";
                        color: Theme.foreground;
                        font-size: Theme.font-size-sm;
                    }
                }
            }
            g1 := ResizeHandle {
                split: 200px;
                minimum: 120px;
            }
            ResizablePane {
                horizontal-stretch: 1;
                VerticalLayout {
                    padding: 12px;
                    Text {
                        text: "Centre";
                        color: Theme.foreground;
                        font-size: Theme.font-size-sm;
                    }
                }
            }
        }
    }
    ResizablePane {
        vertical-stretch: 1;
        HorizontalLayout {
            spacing: 0;
            ResizablePane {
                width: g2.split;
                VerticalLayout {
                    padding: 12px;
                    Text {
                        text: "Bottom left";
                        color: Theme.foreground;
                        font-size: Theme.font-size-sm;
                    }
                }
            }
            g2 := ResizeHandle {
                split: 200px;
                minimum: 120px;
            }
            ResizablePane {
                horizontal-stretch: 1;
                VerticalLayout {
                    padding: 12px;
                    Text {
                        text: "Bottom right";
                        color: Theme.foreground;
                        font-size: Theme.font-size-sm;
                    }
                }
            }
        }
    }
}
// A column of two rows, each of which is itself a horizontal split.
let groups = column![
    ResizablePane::new().child(
        row![
            ResizablePane::new()
                .width(app.group_top)
                .child(pane("Left", "")),
            handle(Slot::GroupTop, app.group_top).minimum(120.0),
            ResizablePane::new().child(pane("Centre", "")),
        ]
        .width(Length::Fill)
        .height(Length::Fill),
    ),
    ResizablePane::new().child(
        row![
            ResizablePane::new()
                .width(app.group_bottom)
                .child(pane("Bottom left", "")),
            handle(Slot::GroupBottom, app.group_bottom).minimum(120.0),
            ResizablePane::new().child(pane("Bottom right", "")),
        ]
        .width(Length::Fill)
        .height(Length::Fill),
    ),
]
.width(Length::Fill)
.height(240.0)
.spacing(0.0);

#Conditional Panel Visibility

resizable_panel()
    .visible(self.show_sidebar)
    .size(px(250.))
    .child("Sidebar Content")
VerticalLayout {
    spacing: 10px;
    HorizontalLayout {
        spacing: 8px;
        alignment: start;
        Button {
            text: root.show-aside ? "Hide the sidebar" : "Show the sidebar";
            outline: true;
            clicked => {
                root.show-aside = !root.show-aside;
            }
        }
    }
    HorizontalLayout {
        height: 140px;
        spacing: 0;
        if root.show-aside: ResizablePane {
            width: root.aside-split;
            VerticalLayout {
                padding: 12px;
                Text {
                    text: "Sidebar";
                    color: Theme.foreground;
                    font-size: Theme.font-size-sm;
                    font-weight: 600;
                }
            }
        }
        if root.show-aside: ResizeHandle {
            split <=> root.aside-split;
            minimum: 120px;
        }
        ResizablePane {
            horizontal-stretch: 1;
            VerticalLayout {
                padding: 12px;
                Text {
                    text: "Content";
                    color: Theme.foreground;
                    font-size: Theme.font-size-sm;
                    font-weight: 600;
                }
            }
        }
    }
}
// A pane can be left out entirely; the handle stays with it and the
// remaining pane keeps the width the hidden one gave back.
let visibility = column![
    Button::new(if app.show_aside {
        "Hide the sidebar"
    } else {
        "Show the sidebar"
    })
    .outline()
    .on_press(Message::ToggleAside),
    row![]
        .extend(app.show_aside.then(|| {
            Element::from(ResizablePane::new().width(app.aside).child(pane("Sidebar", "")))
        }))
        .extend(app.show_aside.then(|| {
            Element::from(handle(Slot::Aside, app.aside).minimum(120.0))
        }))
        .push(ResizablePane::new().child(pane("Content", "")))
        .width(Length::Fill)
        .height(140.0)
        .spacing(0.0),
]
.spacing(10.0)
.align_x(Alignment::Start);

#Panel with Size Limits

// Panel with minimum size only
resizable_panel()
    .size_range(px(100.)..Pixels::MAX)
    .child("Flexible Panel")

// Panel with both min and max
resizable_panel()
    .size_range(px(200.)..px(500.))
    .child("Constrained Panel")

// Panel with exact constraints
resizable_panel()
    .size(px(300.))
    .size_range(px(300.)..px(300.))  // Fixed size
    .child("Fixed Panel")
HorizontalLayout {
    height: 160px;
    spacing: 0;
    ResizablePane {
        width: u3.split;
        VerticalLayout {
            padding: 12px;
            spacing: 4px;
            Text {
                text: "Capped pane";
                color: Theme.foreground;
                font-size: Theme.font-size-sm;
            }
            Text {
                text: "120px .. 320px.";
                color: Theme.muted-foreground;
                font-size: Theme.font-size-xs;
            }
        }
    }
    u3 := ResizeHandle {
        split: 200px;
        minimum: 120px;
        maximum: 320px;
    }
    ResizablePane {
        horizontal-stretch: 1;
        VerticalLayout {
            padding: 12px;
            Text {
                text: "Content";
                color: Theme.foreground;
                font-size: Theme.font-size-sm;
            }
        }
    }
}
// A ceiling as well as a floor: the pane cannot grow past 320px either.
let limits = row![
    ResizablePane::new()
        .width(app.limits)
        .child(pane("Capped pane", "120px .. 320px.")),
    handle(Slot::Limits, app.limits)
        .minimum(120.0)
        .maximum(320.0),
    ResizablePane::new().child(pane("Content", "")),
]
.width(Length::Fill)
.height(160.0);

#File Explorer Layout

struct FileExplorer {
    show_sidebar: bool,
}

impl Render for FileExplorer {
    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        h_resizable("file-explorer", window, cx)
            .child(
                resizable_panel()
                    .visible(self.show_sidebar)
                    .size(px(250.))
                    .size_range(px(200.)..px(400.))
                    .child(
                        v_flex()
                            .p_4()
                            .child("📁 Folders")
                            .child("• Documents")
                            .child("• Pictures")
                            .child("• Downloads")
                    )
            )
            .child(
                v_flex()
                    .p_4()
                    .child("📄 Files")
                    .child("file1.txt")
                    .child("file2.pdf")
                    .child("image.png")
                    .into_any_element()
            )
    }
}
HorizontalLayout {
    height: 220px;
    spacing: 0;
    ResizablePane {
        width: f1.split;
        VerticalLayout {
            padding: 12px;
            spacing: 6px;
            Text {
                text: "src";
                color: Theme.foreground;
                font-size: Theme.font-size-sm;
                font-weight: 600;
            }
            for row in [
                "components",
                "lib.rs",
                "main.rs",
                "theme.rs"
            ]
            : Text {
                text: row;
                color: Theme.muted-foreground;
                font-size: Theme.font-size-xs;
            }
        }
    }
    f1 := ResizeHandle {
        split: 200px;
        minimum: 140px;
    }
    ResizablePane {
        horizontal-stretch: 1;
        VerticalLayout {
            padding: 12px;
            spacing: 6px;
            Text {
                text: "main.rs";
                color: Theme.foreground;
                font-size: Theme.font-size-sm;
                font-weight: 600;
            }
            for line in [
                "fn main() {",
                "    App::new().run();",
                "}"
            ]
            : Text {
                text: line;
                color: Theme.muted-foreground;
                font-size: Theme.font-size-xs;
            }
        }
    }
}
// A file tree beside the file it opens.
let explorer = row![
    ResizablePane::new().width(app.explorer).child(list_pane(
        "src",
        &["components", "lib.rs", "main.rs", "theme.rs"],
    )),
    handle(Slot::Explorer, app.explorer).minimum(140.0),
    ResizablePane::new().child(list_pane(
        "main.rs",
        &["fn main() {", "    App::new().run();", "}"],
    )),
]
.width(Length::Fill)
.height(220.0);

#IDE Layout

struct IDELayout {
    main_state: Entity<ResizableState>,
    sidebar_state: Entity<ResizableState>,
    bottom_state: Entity<ResizableState>,
}

impl Render for IDELayout {
    fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
        h_resizable("ide-main", self.main_state.clone())
            .child(
                resizable_panel()
                    .size(px(300.))
                    .size_range(px(200.)..px(500.))
                    .child(
                        v_resizable("sidebar", self.sidebar_state.clone())
                            .child(
                                resizable_panel()
                                    .size(px(200.))
                                    .child("File Explorer")
                            )
                            .child(
                                resizable_panel()
                                    .child("Outline")
                            )
                    )
            )
            .child(
                resizable_panel()
                    .child(
                        v_resizable("editor-area", self.bottom_state.clone())
                            .child(
                                resizable_panel()
                                    .child("Code Editor")
                            )
                            .child(
                                resizable_panel()
                                    .size(px(150.))
                                    .size_range(px(100.)..px(300.))
                                    .child("Terminal / Output")
                            )
                    )
            )
    }
}
SlintNo Slint version of this example yet.
// A workspace: a sidebar beside an editor, and the editor split again into
// the code above and the terminal below, with a vertical handle between them.
let ide = row![
    ResizablePane::new()
        .width(app.ide_sidebar)
        .child(pane("File Explorer", "")),
    handle(Slot::IdeSidebar, app.ide_sidebar).minimum(200.0),
    ResizablePane::new().child(
        column![
            ResizablePane::new().child(pane("Code Editor", "")),
            handle(Slot::IdeTerminal, app.ide_terminal)
                .minimum(100.0)
                .axis(Axis::Vertical),
            ResizablePane::new()
                .height(app.ide_terminal)
                .child(pane("Terminal / Output", "")),
        ]
        .width(Length::Fill)
        .height(Length::Fill)
        .spacing(0.0),
    ),
]
.width(Length::Fill)
.height(240.0);

#Dashboard with Widgets

struct Dashboard {
    layout_state: Entity<ResizableState>,
    widget_state: Entity<ResizableState>,
}

impl Render for Dashboard {
    fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
        v_resizable("dashboard", self.layout_state.clone())
            .child(
                resizable_panel()
                    .size(px(120.))
                    .child("Header / Navigation")
            )
            .child(
                resizable_panel()
                    .child(
                        h_resizable("widgets", self.widget_state.clone())
                            .child(
                                resizable_panel()
                                    .size(px(300.))
                                    .child("Chart Widget")
                            )
                            .child(
                                resizable_panel()
                                    .child("Data Table")
                            )
                            .child(
                                resizable_panel()
                                    .size(px(250.))
                                    .child("Stats Panel")
                            )
                    )
            )
            .child(
                resizable_panel()
                    .size(px(60.))
                    .child("Footer")
            )
    }
}
HorizontalLayout {
    height: 240px;
    spacing: 0;
    ResizablePane {
        width: d1.split;
        VerticalLayout {
            height: 100%;
            spacing: 0;
            ResizablePane {
                vertical-stretch: 1;
                VerticalLayout {
                    padding: 12px;
                    Text {
                        text: "Revenue";
                        color: Theme.foreground;
                        font-size: Theme.font-size-sm;
                        font-weight: 600;
                    }
                }
            }
            ResizablePane {
                vertical-stretch: 1;
                VerticalLayout {
                    padding: 12px;
                    Text {
                        text: "Signups";
                        color: Theme.foreground;
                        font-size: Theme.font-size-sm;
                        font-weight: 600;
                    }
                }
            }
        }
    }
    d1 := ResizeHandle {
        split: 260px;
        minimum: 160px;
    }
    ResizablePane {
        horizontal-stretch: 1;
        VerticalLayout {
            height: 100%;
            spacing: 0;
            ResizablePane {
                vertical-stretch: 1;
                VerticalLayout {
                    padding: 12px;
                    Text {
                        text: "Traffic";
                        color: Theme.foreground;
                        font-size: Theme.font-size-sm;
                        font-weight: 600;
                    }
                }
            }
            ResizablePane {
                vertical-stretch: 1;
                VerticalLayout {
                    padding: 12px;
                    Text {
                        text: "Errors";
                        color: Theme.foreground;
                        font-size: Theme.font-size-sm;
                        font-weight: 600;
                    }
                }
            }
        }
    }
}
// Four widgets: a row of two splits, each split again into two rows.
let dashboard = row![
    ResizablePane::new().width(app.dashboard).child(
        column![
            ResizablePane::new().child(pane("Revenue", "")),
            ResizablePane::new().child(pane("Signups", "")),
        ]
        .width(Length::Fill)
        .height(Length::Fill)
        .spacing(0.0),
    ),
    handle(Slot::Dashboard, app.dashboard).minimum(160.0),
    ResizablePane::new().child(
        column![
            ResizablePane::new().child(pane("Traffic", "")),
            ResizablePane::new().child(pane("Errors", "")),
        ]
        .width(Length::Fill)
        .height(Length::Fill)
        .spacing(0.0),
    ),
]
.width(Length::Fill)
.height(240.0);

#Settings Panel

struct SettingsPanel {
    settings_state: Entity<ResizableState>,
}

impl SettingsPanel {
    fn new(cx: &mut Context<Self>) -> Self {
        let settings_state = ResizableState::new(cx);

        // Listen for resize events to save layout preferences
        cx.subscribe(&settings_state, |this, _, event: &ResizablePanelEvent, cx| {
            match event {
                ResizablePanelEvent::Resized => {
                    this.save_layout_preferences(cx);
                }
            }
        });

        Self { settings_state }
    }

    fn save_layout_preferences(&self, cx: &mut Context<Self>) {
        let sizes = self.settings_state.read(cx).sizes();
        // Save to preferences
        println!("Saving layout: {:?}", sizes);
    }
}

impl Render for SettingsPanel {
    fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
        h_resizable("settings", self.settings_state.clone())
            .child(
                resizable_panel()
                    .size(px(200.))
                    .size_range(px(150.)..px(300.))
                    .child(
                        v_flex()
                            .gap_2()
                            .p_4()
                            .child("Categories")
                            .child("• General")
                            .child("• Appearance")
                            .child("• Advanced")
                    )
            )
            .child(
                resizable_panel()
                    .child(
                        div()
                            .p_6()
                            .child("Settings Content Area")
                    )
            )
    }
}
HorizontalLayout {
    height: 200px;
    spacing: 0;
    ResizablePane {
        width: s1.split;
        VerticalLayout {
            padding: 12px;
            spacing: 6px;
            for row in [
                "General",
                "Appearance",
                "Shortcuts",
                "Updates"
            ]
            : Text {
                text: row;
                color: Theme.muted-foreground;
                font-size: Theme.font-size-sm;
            }
        }
    }
    s1 := ResizeHandle {
        split: 180px;
        minimum: 140px;
    }
    ResizablePane {
        horizontal-stretch: 1;
        VerticalLayout {
            padding: 12px;
            spacing: 4px;
            Text {
                text: "Appearance";
                color: Theme.foreground;
                font-size: Theme.font-size-sm;
                font-weight: 600;
            }
            Text {
                text: "The panel the navigation selects.";
                color: Theme.muted-foreground;
                font-size: Theme.font-size-xs;
            }
        }
    }
}
// A navigation column beside the panel it selects.
let settings = row![
    ResizablePane::new().width(app.settings).child(list_pane(
        "",
        &["General", "Appearance", "Shortcuts", "Updates"],
    )),
    handle(Slot::Settings, app.settings).minimum(140.0),
    ResizablePane::new().child(pane(
        "Appearance",
        "The panel the navigation selects.",
    )),
]
.width(Length::Fill)
.height(200.0);

#Best Practices

  1. State Management: Use separate ResizableState for independent layouts
  2. Size Constraints: Always set reasonable min/max sizes for panels
  3. Event Handling: Subscribe to ResizablePanelEvent for layout persistence
  4. Nested Layouts: Use .group() method for clean nested structures
  5. Performance: Avoid excessive nesting for better performance
  6. User Experience: Provide adequate handle padding for easier interaction