Full documentation: https://flexlayout.2plot.dev — the dedicated flexlayout-dash documentation site, with the complete API reference and deeper examples. This page is the quick-start overview.
flexlayout-dash is a Dash component library that provides a flexible, dock-able layout system for building professional dashboard interfaces. It features resizable and draggable panels, floating windows with pop-out support, multiple layout types (horizontal, vertical, nested), collapsible sidebar borders, theme integration with Mantine, state persistence, and extensive customization options for creating IDE-style layouts, dashboards, and complex multi-panel applications.
Installation
⭐️ Star this component on GitHub! Stay up to date on new releases and browse the codebase.
pip install flexlayout-dash
Requirements:
- Python >= 3.7
- Dash >= 2.0
- dash-mantine-components (recommended for theming)
Quick Start
Create a simple two-panel layout with resizable divider.
# File: docs/flexlayout_dash/introduction.py
"""
Dash Flex Layout - Quick Start Example
======================================
A simple two-panel layout with resizable divider.
"""
import dash_mantine_components as dmc
import flexlayout_dash as dfl
# Define the layout model with 50/50 horizontal split
model = {
"global": {
"tabEnableClose": False, # Prevent closing tabs
"tabEnableRenderOnDemand": False, # Keep all tabs mounted for callbacks
},
"layout": {
"type": "row", # Horizontal split
"children": [
{
"type": "tabset",
"weight": 50, # 50% of width
"children": [
{
"type": "tab",
"name": "Left Panel",
"id": "left-panel",
}
]
},
{
"type": "tabset",
"weight": 50, # 50% of width
"children": [
{
"type": "tab",
"name": "Right Panel",
"id": "right-panel",
}
]
}
]
}
}
# Create tab components with content
tabs = [
dfl.Tab(
id="left-panel",
children=[
dmc.Stack([
dmc.Title("Welcome to Flex Layout", order=3),
dmc.Text(
"This is a simple two-panel layout with a resizable divider. "
"Drag the divider in the middle to adjust panel sizes.",
size="sm"
),
dmc.Badge("Left Panel", color="blue", size="lg", mt="md"),
], p="md")
]
),
dfl.Tab(
id="right-panel",
children=[
dmc.Stack([
dmc.Title("Right Panel", order=3),
dmc.Text(
"Panels automatically adjust their size based on the weight property. "
"Both panels have equal weight (50), so they split evenly.",
size="sm"
),
dmc.Badge("Right Panel", color="green", size="lg", mt="md"),
], p="md")
]
),
]
# Main component for rendering
component = dmc.Stack([
dmc.Alert(
children="Try resizing the panels by dragging the divider!",
title="Quick Start",
color="blue",
),
dmc.Box(
style={"height": "400px", "width": "100%", "position": "relative"},
children=dfl.DashFlexLayout(
id='flex-layout-intro',
model=model,
children=tabs,
style={"height": "100%", "width": "100%"},
useStateForModel=True,
supportsPopout=False,
)
)
], gap="md")
Two-Panel Layouts
Split your interface horizontally or vertically using the weight system. The weight determines how much space each panel occupies relative to others.
import flexlayout_dash as dfl
# 50/50 split
model = {
"global": {"tabEnableClose": False, "tabEnableRenderOnDemand": False},
"layout": {
"type": "row", # Horizontal split
"children": [
{"type": "tabset", "weight": 50, "children": [{"type": "tab", "name": "Left", "id": "left"}]},
{"type": "tabset", "weight": 50, "children": [{"type": "tab", "name": "Right", "id": "right"}]}
]
}
}
# Create tabs
tabs = [
dfl.Tab(id="left", children=[...]),
dfl.Tab(id="right", children=[...])
]
# Render layout
dfl.DashFlexLayout(
id='layout',
model=model,
children=tabs,
useStateForModel=True,
supportsPopout=False,
)
Weight System:
- Weights are relative values (e.g., 70/30 creates a 70%-30% split)
- Total weights don't need to sum to 100
- Adjust weights to create custom proportions (e.g., 2:1 ratio = weights 66:33)
Multiple Tabs
Organize multiple views within a single panel using tabs. Users can click tabs to switch between different content.
# File: docs/flexlayout_dash/two_panel_layout.py
"""
Dash Flex Layout - Multiple Tabs Example
=========================================
Single tabset with multiple tabs for navigation.
"""
import dash_mantine_components as dmc
import flexlayout_dash as dfl
from dash_iconify import DashIconify
# Define the layout model with single tabset
model = {
"global": {
"tabEnableClose": False,
"tabEnableFloat": True, # Allow floating tabs
"tabEnableRenderOnDemand": False, # Keep all tabs mounted
},
"layout": {
"type": "row",
"children": [
{
"type": "tabset",
"weight": 100, # Full width
"selected": 0, # Initially selected tab (Overview)
"children": [
{"type": "tab", "name": "Overview", "id": "tab-overview"},
{"type": "tab", "name": "Details", "id": "tab-details"},
{"type": "tab", "name": "Settings", "id": "tab-settings"}
]
}
]
}
}
# Create tab components
tabs = [
dfl.Tab(
id="tab-overview",
children=[
dmc.Card([
dmc.Group([
DashIconify(icon="mdi:view-dashboard", width=32, color="blue"),
dmc.Title("Overview", order=3)
], gap="sm"),
dmc.Text(
"This tab shows an overview of your data. Multiple tabs allow you to organize "
"different views within the same panel.",
size="sm",
mt="md"
),
dmc.SimpleGrid([
dmc.Paper([
dmc.Text("Total Users", size="xs", c="dimmed"),
dmc.Title("1,234", order=2),
], p="md", withBorder=True),
dmc.Paper([
dmc.Text("Active Now", size="xs", c="dimmed"),
dmc.Title("567", order=2),
], p="md", withBorder=True),
dmc.Paper([
dmc.Text("Revenue", size="xs", c="dimmed"),
dmc.Title("$12.3k", order=2),
], p="md", withBorder=True),
], cols=3, mt="xl")
], p="xl")
]
),
dfl.Tab(
id="tab-details",
children=[
dmc.Card([
dmc.Group([
DashIconify(icon="mdi:information-outline", width=32, color="green"),
dmc.Title("Details", order=3)
], gap="sm"),
dmc.Text(
"Detailed information and analytics are shown in this tab. "
"Switch between tabs to see different content.",
size="sm",
mt="md"
),
dmc.Stack([
dmc.Text("• Flexible tab navigation", size="sm"),
dmc.Text("• Organize content into logical groups", size="sm"),
dmc.Text("• Easy to understand structure", size="sm"),
dmc.Text("• Customizable tab names and icons", size="sm"),
], gap="xs", mt="xl")
], p="xl")
]
),
dfl.Tab(
id="tab-settings",
children=[
dmc.Card([
dmc.Group([
DashIconify(icon="mdi:cog-outline", width=32, color="orange"),
dmc.Title("Settings", order=3)
], gap="sm"),
dmc.Text(
"Configuration and preferences go here. Each tab can contain "
"any Dash components you need.",
size="sm",
mt="md"
),
dmc.Stack([
dmc.Switch(label="Enable notifications", checked=True, mt="lg"),
dmc.Switch(label="Auto-save changes", checked=False),
dmc.Switch(label="Dark mode", checked=False),
], gap="md", mt="xl")
], p="xl")
]
),
]
# Main component for rendering
component = dmc.Stack([
dmc.Alert(
children="Click on different tabs to navigate between views!",
title="Multiple Tabs",
color="teal",
),
dmc.Box(
style={"height": "450px", "width": "100%", "position": "relative"},
children=dfl.DashFlexLayout(
id='flex-layout-tabs',
model=model,
children=tabs,
style={"height": "100%", "width": "100%"},
useStateForModel=True,
supportsPopout=False,
)
)
], gap="md")
Tab Configuration:
- Set
selected: 0in tabset to choose initially selected tab (zero-indexed) - Enable
tabEnableFloat: Trueto allow tabs to be popped out into floating windows - Each tab needs a unique
idthat matches a Tab component
Vertical Layouts
Use "type": "column" to create vertical splits with top/bottom panels.
model = {
"global": {"tabEnableClose": False, "tabEnableRenderOnDemand": False},
"layout": {
"type": "column", # Vertical split
"children": [
{"type": "tabset", "weight": 40, "children": [{"type": "tab", "name": "Top", "id": "top"}]},
{"type": "tabset", "weight": 60, "children": [{"type": "tab", "name": "Bottom", "id": "bottom"}]}
]
}
}
Use Cases:
- Dashboard with chart on top and data table below
- Editor with preview panel underneath
- Main content with footer panel
Nested Layouts
Combine rows and columns to create complex multi-panel interfaces. Perfect for IDE-style layouts and advanced dashboards.
# File: docs/flexlayout_dash/ide_layout.py
"""
Dash Flex Layout - Nested Layout Example
==========================================
Simple nested layout demonstrating rows within columns.
"""
import dash_mantine_components as dmc
import flexlayout_dash as dfl
# Define nested layout model (no borders for simplicity)
model = {
"global": {
"tabEnableClose": False,
"tabEnableRenderOnDemand": False,
},
"layout": {
"type": "row",
"children": [
{
"type": "column",
"weight": 70,
"children": [
{
"type": "tabset",
"weight": 60,
"children": [
{"type": "tab", "name": "Editor", "id": "editor"}
]
},
{
"type": "tabset",
"weight": 40,
"children": [
{"type": "tab", "name": "Terminal", "id": "terminal"}
]
}
]
},
{
"type": "tabset",
"weight": 30,
"children": [
{"type": "tab", "name": "Preview", "id": "preview"}
]
}
]
}
}
# Create tab components
tabs = [
# Editor Tab (top left)
dfl.Tab(
id="editor",
children=[
dmc.Box(
p="md",
children=[
dmc.Title("Code Editor", order=4, mb="md"),
dmc.Code(
block=True,
children="""from dash import Dash
import dash_mantine_components as dmc
app = Dash(__name__)
app.layout = dmc.Container([
dmc.Title("Hello Dash"),
dmc.Text("Nested layouts!")
])
if __name__ == '__main__':
app.run(debug=True)""",
style={
"fontSize": "13px",
}
)
]
)
]
),
# Terminal Tab (bottom left)
dfl.Tab(
id="terminal",
children=[
dmc.Box(
p="md",
children=[
dmc.Title("Terminal", order=4, mb="md"),
dmc.Code(
block=True,
children="""$ python app.py
Dash is running on http://127.0.0.1:8050/
* Debug mode: on""",
style={
"fontSize": "13px",
"backgroundColor": "var(--mantine-color-dark-6)",
"color": "var(--mantine-color-green-4)",
}
)
]
)
]
),
# Preview Tab (right side)
dfl.Tab(
id="preview",
children=[
dmc.Box(
p="md",
children=[
dmc.Title("Live Preview", order=4, mb="md"),
dmc.Paper(
p="xl",
withBorder=True,
children=[
dmc.Title("Hello Dash", order=2, mb="xs"),
dmc.Text("Nested layouts!", c="dimmed")
]
)
]
)
]
),
]
# Main component for rendering
component = dmc.Stack(
gap="md",
children=[
dmc.Alert(
color="violet",
variant="light",
children=[
dmc.Text("Nested Layouts", fw=700, mb="xs"),
dmc.Text("Nested layout with a vertical split on the left (Editor/Terminal) and Preview panel on the right.")
]
),
dmc.Box(
style={"height": "500px", "width": "100%", "position": "relative"},
children=dfl.DashFlexLayout(
id='flex-layout-nested',
model=model,
children=tabs,
style={"height": "100%", "width": "100%"},
useStateForModel=True,
supportsPopout=False,
)
)
]
)
Nested Example:
model = {
"layout": {
"type": "row",
"children": [
{
"type": "column", # Nested vertical split
"weight": 70,
"children": [
{"type": "tabset", "weight": 60, "children": [...]},
{"type": "tabset", "weight": 40, "children": [...]}
]
},
{"type": "tabset", "weight": 30, "children": [...]} # Right sidebar
]
}
}
Borders (Sidebars)
Create collapsible sidebars on the left, right, or bottom edges using borders. Perfect for navigation, tools, or secondary content.
model = {
"global": {...},
"borders": [
{
"type": "border",
"location": "left", # "left", "right", or "bottom"
"size": 250, # Width in pixels
"selected": 0, # Initially selected tab
"children": [
{"type": "tab", "name": "Files", "id": "files"},
{"type": "tab", "name": "Search", "id": "search"}
]
}
],
"layout": {
"type": "row",
"children": [...] # Main content area
}
}
Border Features:
- Click border button to expand/collapse
- Support left, right, and bottom positions (Note: top borders not supported)
- Can contain multiple tabs like regular tabsets
- Size in pixels (width for left/right, height for bottom)
Theme Integration
Flex Layout automatically detects your app's color scheme from MantineProvider and adjusts colors accordingly.
# File: docs/flexlayout_dash/theming_example.py
"""
Liquid Glass Themes Example for Dash Flex Layout
=================================================
Showcases Apple WWDC 2025 inspired liquid glass design with light/dark themes.
This example is completely isolated and won't affect other DashFlexLayout instances.
"""
from dash import callback, Input, Output
import dash_mantine_components as dmc
from dash_iconify import DashIconify
import flexlayout_dash as dfl
# Inline style definitions for glass effects
GLASS_CONTAINER_STYLE_DARK = {
"background": "linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%)",
"borderRadius": "16px",
"padding": "4px",
"position": "relative",
"transition": "background 0.3s ease",
}
GLASS_CONTAINER_STYLE_LIGHT = {
"background": "linear-gradient(135deg, #e8f4f8 0%, #d4e5f7 50%, #c1d5eb 100%)",
"borderRadius": "16px",
"padding": "4px",
"position": "relative",
"transition": "background 0.3s ease",
}
GLASS_PANEL_STYLE_DARK = {
"backdropFilter": "blur(20px) saturate(180%)",
"WebkitBackdropFilter": "blur(20px) saturate(180%)",
"background": "linear-gradient(135deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0.05) 100%)",
"border": "1px solid rgba(255, 255, 255, 0.15)",
"boxShadow": "0 8px 32px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.1)",
"borderRadius": "12px",
"padding": "1rem",
"height": "100%",
"overflow": "auto",
}
GLASS_PANEL_STYLE_LIGHT = {
"backdropFilter": "blur(20px) saturate(180%)",
"WebkitBackdropFilter": "blur(20px) saturate(180%)",
"background": "linear-gradient(135deg, rgba(255, 255, 255, 0.7) 0%, rgba(255, 255, 255, 0.5) 100%)",
"border": "1px solid rgba(255, 255, 255, 0.5)",
"boxShadow": "0 8px 32px rgba(31, 38, 135, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.8)",
"borderRadius": "12px",
"padding": "1rem",
"height": "100%",
"overflow": "auto",
}
FEATURE_CARD_STYLE_DARK = {
"backdropFilter": "blur(16px) saturate(160%)",
"WebkitBackdropFilter": "blur(16px) saturate(160%)",
"background": "rgba(45, 55, 72, 0.6)",
"border": "1px solid rgba(255, 255, 255, 0.1)",
"borderRadius": "10px",
"padding": "1rem",
"marginBottom": "0.5rem",
}
FEATURE_CARD_STYLE_LIGHT = {
"backdropFilter": "blur(16px) saturate(160%)",
"WebkitBackdropFilter": "blur(16px) saturate(160%)",
"background": "rgba(255, 255, 255, 0.7)",
"border": "1px solid rgba(0, 0, 0, 0.05)",
"borderRadius": "10px",
"padding": "1rem",
"marginBottom": "0.5rem",
}
# Define layout model
model = {
"global": {
"tabEnableClose": False,
"tabEnableRenderOnDemand": False,
},
"layout": {
"type": "row",
"children": [
{
"type": "tabset",
"weight": 30,
"children": [
{"type": "tab", "name": "Overview", "id": "theme-overview"}
]
},
{
"type": "tabset",
"weight": 40,
"children": [
{"type": "tab", "name": "Features", "id": "theme-features"}
]
},
{
"type": "tabset",
"weight": 30,
"children": [
{"type": "tab", "name": "Metrics", "id": "theme-metrics"}
]
}
]
}
}
def create_overview_panel(theme="dark"):
"""Create overview panel with theme switcher."""
glass_style = GLASS_PANEL_STYLE_DARK if theme == "dark" else GLASS_PANEL_STYLE_LIGHT
return dmc.Box(
id="theme-overview",
style=glass_style,
children=[
dmc.Stack(
gap="md",
children=[
dmc.Group(
justify="space-between",
children=[
dmc.Group(
gap="sm",
children=[
DashIconify(icon="mdi:palette-advanced", width=32, color="#4FC3F7"),
dmc.Stack(
gap=0,
children=[
dmc.Title("Liquid Glass", order=4),
dmc.Text("Apple WWDC 2025", size="xs", c="dimmed"),
]
),
]
),
dmc.SegmentedControl(
id="flex-theme-switcher",
data=[
{"value": "dark", "label": "🌙 Dark"},
{"value": "light", "label": "☀️ Light"},
],
value=theme,
color="blue",
radius="lg",
size="sm",
),
]
),
dmc.Divider(opacity=0.2),
dmc.Text(
"Experience Apple's next-generation glassmorphism design with backdrop blur, translucency, and gradient overlays.",
size="sm",
c="dimmed",
),
dmc.Group(
gap="xs",
mt="sm",
children=[
dmc.Badge("20px Blur", variant="dot", color="cyan", size="sm"),
dmc.Badge("180% Saturation", variant="dot", color="violet", size="sm"),
dmc.Badge("Glass Morphism", variant="dot", color="orange", size="sm"),
]
),
]
)
]
)
def create_features_panel(theme="dark"):
"""Create features showcase panel."""
card_style = FEATURE_CARD_STYLE_DARK if theme == "dark" else FEATURE_CARD_STYLE_LIGHT
return dmc.Box(
id="theme-features",
style=GLASS_PANEL_STYLE_DARK if theme == "dark" else GLASS_PANEL_STYLE_LIGHT,
children=[
dmc.Stack(
gap="sm",
children=[
dmc.Title("Key Features", order=5, mb="xs"),
dmc.Box(
style=card_style,
children=[
dmc.Group(
gap="sm",
mb="xs",
children=[
DashIconify(icon="mdi:blur", width=18, color="#4FC3F7"),
dmc.Text("Backdrop Blur", fw=600, size="sm"),
]
),
dmc.Text(
"Advanced blur effects with 20px radius and 180% saturation.",
size="xs",
c="dimmed",
),
]
),
dmc.Box(
style=card_style,
children=[
dmc.Group(
gap="sm",
mb="xs",
children=[
DashIconify(icon="mdi:opacity", width=18, color="#9C27B0"),
dmc.Text("Translucency", fw=600, size="sm"),
]
),
dmc.Text(
"Semi-transparent gradients that adapt to light and dark modes.",
size="xs",
c="dimmed",
),
]
),
dmc.Box(
style=card_style,
children=[
dmc.Group(
gap="sm",
mb="xs",
children=[
DashIconify(icon="mdi:gradient-vertical", width=18, color="#FFC107"),
dmc.Text("Liquid Borders", fw=600, size="sm"),
]
),
dmc.Text(
"Smooth gradient borders with animated effects.",
size="xs",
c="dimmed",
),
]
),
dmc.Box(
style=card_style,
children=[
dmc.Group(
gap="sm",
mb="xs",
children=[
DashIconify(icon="mdi:theme-light-dark", width=18, color="#00BCD4"),
dmc.Text("Theme Aware", fw=600, size="sm"),
]
),
dmc.Text(
"Automatically adapts to light and dark color schemes.",
size="xs",
c="dimmed",
),
]
),
]
)
]
)
def create_metrics_panel(theme="dark"):
"""Create metrics panel."""
card_style = FEATURE_CARD_STYLE_DARK if theme == "dark" else FEATURE_CARD_STYLE_LIGHT
return dmc.Box(
id="theme-metrics",
style=GLASS_PANEL_STYLE_DARK if theme == "dark" else GLASS_PANEL_STYLE_LIGHT,
children=[
dmc.Stack(
gap="md",
children=[
dmc.Title("Design Metrics", order=5),
dmc.Box(
style=card_style,
children=[
dmc.Group(
justify="space-between",
align="center",
children=[
dmc.Stack(
gap=0,
children=[
dmc.Text("Blur Radius", size="xs", c="dimmed"),
dmc.Title("20px", order=4, c="cyan"),
]
),
DashIconify(icon="mdi:blur-radial", width=28, color="#4FC3F7", style={"opacity": 0.6}),
]
),
]
),
dmc.Box(
style=card_style,
children=[
dmc.Group(
justify="space-between",
align="center",
children=[
dmc.Stack(
gap=0,
children=[
dmc.Text("Saturation", size="xs", c="dimmed"),
dmc.Title("180%", order=4, c="violet"),
]
),
DashIconify(icon="mdi:palette", width=28, color="#9C27B0", style={"opacity": 0.6}),
]
),
]
),
dmc.Box(
style=card_style,
children=[
dmc.Group(
justify="space-between",
align="center",
children=[
dmc.Stack(
gap=0,
children=[
dmc.Text("Opacity", size="xs", c="dimmed"),
dmc.Title("65%", order=4, c="orange"),
]
),
DashIconify(icon="mdi:opacity", width=28, color="#FFC107", style={"opacity": 0.6}),
]
),
]
),
]
)
]
)
# Create tabs with default dark theme
tabs_dark = [
dfl.Tab(id="theme-overview", children=[create_overview_panel("dark")]),
dfl.Tab(id="theme-features", children=[create_features_panel("dark")]),
dfl.Tab(id="theme-metrics", children=[create_metrics_panel("dark")]),
]
# Main component
component = dmc.Stack(
gap="md",
children=[
dmc.Alert(
color="indigo",
variant="light",
children=[
dmc.Text("Liquid Glass Theme Demo", fw=700, mb="xs"),
dmc.Text("Toggle between light and dark themes to see the glassmorphism effect adapt dynamically!")
]
),
dmc.Box(
id="flex-glass-container",
style=GLASS_CONTAINER_STYLE_DARK,
children=[
dmc.Box(
style={"height": "450px", "position": "relative"},
children=dfl.DashFlexLayout(
id='flex-layout-glass-theme',
model=model,
children=tabs_dark,
style={"height": "100%", "width": "100%"},
useStateForModel=True,
supportsPopout=False,
)
)
]
)
]
)
# Callback to switch themes
@callback(
Output("flex-glass-container", "style"),
Output("flex-layout-glass-theme", "children"),
Input("flex-theme-switcher", "value"),
)
def switch_flex_glass_theme(theme_value):
"""Toggle between liquid glass light and dark themes."""
if theme_value == "light":
tabs = [
dfl.Tab(id="theme-overview", children=[create_overview_panel("light")]),
dfl.Tab(id="theme-features", children=[create_features_panel("light")]),
dfl.Tab(id="theme-metrics", children=[create_metrics_panel("light")]),
]
return GLASS_CONTAINER_STYLE_LIGHT, tabs
else:
tabs = [
dfl.Tab(id="theme-overview", children=[create_overview_panel("dark")]),
dfl.Tab(id="theme-features", children=[create_features_panel("dark")]),
dfl.Tab(id="theme-metrics", children=[create_metrics_panel("dark")]),
]
return GLASS_CONTAINER_STYLE_DARK, tabs
Theme Options:
- Automatic Detection: Component reads theme from MantineProvider context
- Manual Override: Use
colorScheme="light"orcolorScheme="dark"prop - Liquid Glass Themes: Available via custom CSS (see assets/flexlayout-dock.css)
Custom CSS:
/* Apply custom styles to flexlayout */
:root[data-mantine-color-scheme="light"] .flexlayout__layout {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
}
:root[data-mantine-color-scheme="dark"] .flexlayout__layout {
background: rgba(30, 30, 35, 0.95);
backdrop-filter: blur(10px);
}
Interactive Callbacks
Use callbacks to create dynamic, interactive layouts with real-time updates.
# File: docs/flexlayout_dash/callbacks_example.py
"""
Dash Flex Layout - Interactive Callbacks Example
=================================================
Demonstrates callbacks and dynamic content updates.
"""
from dash import callback, Input, Output, dcc
import dash_mantine_components as dmc
import flexlayout_dash as dfl
import json
# Define 3-panel layout model
model = {
"global": {
"tabEnableClose": False,
"tabEnableRenderOnDemand": False, # CRITICAL for callbacks!
},
"layout": {
"type": "row",
"children": [
{
"type": "tabset",
"weight": 30,
"children": [
{"type": "tab", "name": "Controls", "id": "controls-panel"}
]
},
{
"type": "tabset",
"weight": 40,
"children": [
{"type": "tab", "name": "Output", "id": "output-panel"}
]
},
{
"type": "tabset",
"weight": 30,
"children": [
{"type": "tab", "name": "Data", "id": "data-panel"}
]
}
]
}
}
# Create tab components
tabs = [
dfl.Tab(
id="controls-panel",
children=[
dmc.Stack(
p="md",
gap="md",
children=[
dmc.Title("Controls", order=4),
dmc.Select(
id="value-select",
label="Select a value:",
data=[
{"label": "Option 1", "value": "opt1"},
{"label": "Option 2", "value": "opt2"},
{"label": "Option 3", "value": "opt3"},
],
value="opt1"
),
dmc.NumberInput(
id="number-input",
label="Enter a number:",
value=5,
min=1,
max=10,
),
dmc.Button(
"Update Output",
id="update-button",
fullWidth=True,
color="blue"
),
]
)
]
),
dfl.Tab(
id="output-panel",
children=[
dmc.Stack(
p="md",
gap="md",
children=[
dmc.Title("Output", order=4),
dmc.Paper(
id="output-container",
p="md",
withBorder=True,
style={"minHeight": "200px"},
children=[
dmc.Center(
p="xl",
children=dmc.Text("Click 'Update Output' to see results here", c="dimmed")
)
]
),
]
)
]
),
dfl.Tab(
id="data-panel",
children=[
dmc.Stack(
p="md",
gap="md",
children=[
dmc.Title("Data Preview", order=4),
dmc.Code(
id="data-display",
block=True,
children="Click 'Update Output' to see data here",
style={
"fontSize": "12px",
"minHeight": "200px"
}
),
]
)
]
),
]
# Callback to generate data and update output
@callback(
Output("output-container", "children"),
Output("data-display", "children"),
Input("update-button", "n_clicks"),
Input("number-input", "value"),
Input("value-select", "value"),
)
def update_output(n_clicks, number, selected_value):
# Generate output data
data = {
"selected": selected_value,
"number": number,
"result": number * 10
}
# Create output display
output = dmc.Stack(
p="md",
gap="sm",
children=[
dmc.Title(f"Result: {data['result']}", order=3, c="blue"),
dmc.Text(f"You selected: {selected_value}"),
dmc.Text(f"Number entered: {number}"),
dmc.Text(f"Calculation: {number} × 10 = {data['result']}"),
]
)
# Format data display
data_json = json.dumps(data, indent=2)
return output, data_json
# Main component for rendering
component = dmc.Stack(
gap="md",
children=[
dmc.Alert(
color="grape",
variant="light",
children=[
dmc.Text("Interactive Callbacks", fw=700, mb="xs"),
dmc.Text("Use the controls to update the output and data preview. Callbacks work seamlessly with FlexLayout!")
]
),
dmc.Box(
style={"height": "500px", "position": "relative"},
children=dfl.DashFlexLayout(
id='flex-layout-callbacks',
model=model,
children=tabs,
style={"height": "100%", "width": "100%"},
useStateForModel=True,
supportsPopout=False,
)
)
]
)
CRITICAL: Set tabEnableRenderOnDemand: False
model = {
"global": {
"tabEnableRenderOnDemand": False, # Required for callbacks!
},
# ...the rest of the model (layout, borders)...
}
Without this setting, tabs are unmounted when not visible, causing callbacks to fail.
Callback Best Practices:
- Use
useStateForModel=Trueto avoid excessive callback triggers - Define all chart/component types upfront, toggle visibility with styles
- Don't dynamically replace
childrenin callbacks—update props instead - Read layout state via
Input('layout-id', 'model')if needed
Model Configuration
The model prop defines the entire layout structure using a JSON-like dictionary.
Global Settings:
model = {
"global": {
"tabEnableClose": False, # Allow closing tabs
"tabEnableFloat": True, # Allow floating tabs as windows
"tabEnableRenderOnDemand": False, # CRITICAL: Keep all tabs mounted for callbacks
"tabEnableMaximize": True, # Allow maximizing tabsets
"tabEnableDrag": True, # Allow dragging tabs
},
}
Layout Structure:
model = {
"layout": {
"type": "row", # "row" (horizontal) or "column" (vertical)
"weight": 100, # Relative size percentage
"children": [...] # Array of tabsets or nested layouts
},
}
Tabset:
{
"type": "tabset",
"weight": 50, # 50% of parent width/height
"selected": 0, # Initially selected tab index
"children": [...] # Array of tab objects
}
Tab:
{
"type": "tab",
"name": "Tab Name", # Display name
"id": "unique-id", # Must match Tab component id
"enableClose": True, # Override global setting
}
Component Properties
DashFlexLayout Properties
| Property | Type | Default | Description |
|---|---|---|---|
id | string | Required | Unique identifier for the component used in Dash callbacks. |
model | dict | Required | Layout configuration object defining structure, global settings, and borders. |
children | list[Tab] | Required | List of Tab components. Each Tab id must match a tab id in the model. |
headers | dict | None | Custom header components for tabs. Keys are tab IDs, values are Dash components. |
useStateForModel | bool | False | Use internal state management for model. Recommended: True to avoid excessive callbacks. |
colorScheme | string | Auto-detect | Force color scheme: "light" or "dark". Auto-detects from MantineProvider if not set. |
supportsPopout | bool | True | Enable pop-out windows for floating tabs. Set to False to disable. |
popoutURL | string | "/assets/popout.html" | URL for pop-out window HTML template. |
realtimeResize | bool | False | Resize panels in real-time during drag (performance impact). |
debugMode | bool | False | Enable debug logging to console. |
style | dict | None | CSS styles for the container element. |
font | string | None | Custom font family for layout UI. |
loading_state | object | (Dash Internal) | Object describing the loading state of the component. |
Tab Properties
| Property | Type | Default | Description |
|---|---|---|---|
id | string | Required | Unique identifier that must match a tab id in the model configuration. |
children | list | Required | Dash components to render inside the tab panel. |
Best Practices
For Callbacks:
- Always set
tabEnableRenderOnDemand: Falsein global config - Use
useStateForModel=Trueto avoid excessive callback triggers - Define all chart types upfront, toggle visibility with styles
- Don't dynamically replace children in callbacks
For Layout Design:
- Use weight system for responsive sizing (e.g., 70/30 splits)
- Keep nested layouts shallow (max 2-3 levels deep)
- Test mobile responsiveness with smaller weights
- Use borders for collapsible sidebars
For Theming:
- Let component auto-detect Mantine theme when possible
- Use
colorSchemeprop only to force specific theme - Apply custom CSS to
.flexlayout__layoutclass - Test both light and dark modes
Troubleshooting
Callbacks Not Firing:
- Ensure
tabEnableRenderOnDemand: Falsein global config - Verify tab IDs match between model and Tab components
Layout Not Filling Container:
- Set explicit height on parent container
- Example:
style={"height": "100vh"}orh="500px"
Tabs Not Appearing:
- Check that Tab component
idmatches tabidin model - Verify children list includes all Tab components
Theme Not Applying:
- Ensure MantineProvider wraps the layout
- Check that colorScheme prop matches theme
Pop-out Windows Not Working:
- Verify
supportsPopout=True(default) - Check that popoutURL points to valid HTML file
- Ensure HTML file has proper window communication setup
Contributing
Contributions to flexlayout-dash are welcome! Please refer to the project's issues on GitHub for any feature requests or bug reports.
License
This project is licensed under the MIT License.