Agent Skills: Dash Mantine Components (DMC) v2.x

|

UncategorizedID: bjornmelin/dev-skills/dmc-py

Install this agent skill to your local

pnpm dlx add-skill https://github.com/BjornMelin/dev-skills/tree/HEAD/skills/dmc-py

Skill Files

Browse the full folder contents for dmc-py.

Download Skill

Loading file tree…

skills/dmc-py/SKILL.md

Skill Metadata

Name
dmc-py
Description
|

Dash Mantine Components (DMC) v2.x

Build modern Dash applications with 100+ Mantine UI components.

Quick Start

Minimal DMC app requiring MantineProvider wrapper:

from dash import Dash, callback, Input, Output
import dash_mantine_components as dmc

app = Dash(__name__)

app.layout = dmc.MantineProvider([
    dmc.Container([
        dmc.Title("My DMC App", order=1),
        dmc.TextInput(label="Name", id="name-input", placeholder="Enter name"),
        dmc.Button("Submit", id="submit-btn", mt="md"),
        dmc.Text(id="output", mt="md"),
    ], size="sm", py="xl")
])

@callback(Output("output", "children"), Input("submit-btn", "n_clicks"), Input("name-input", "value"))
def update_output(n_clicks, name):
    if not n_clicks:
        return ""
    return f"Hello, {name or 'World'}!"

if __name__ == "__main__":
    app.run(debug=True)

Critical: All DMC components MUST be inside dmc.MantineProvider.

Version Note: This skill targets DMC 2.x (Mantine 8.x). Run pip show dash-mantine-components to check your installed version. For the latest features and API changes, use fetch_docs.py to query the official documentation at https://www.dash-mantine-components.com/assets/llms.txt


Workflow Decision Tree

Select components by use case:

Form Inputs

| Need | Component | Key Props | |------|-----------|-----------| | Text input | TextInput | label, placeholder, value, debounce | | Dropdown | Select | data, value, searchable, clearable | | Multi-select | MultiSelect | data, value, searchable | | Checkbox | Checkbox | label, checked | | Toggle | Switch | label, checked, onLabel, offLabel | | Number | NumberInput | value, min, max, step | | Date | DatePickerInput | value, type, minDate, maxDate | | Rich text | Textarea | label, value, autosize, minRows | | File upload | FileInput | value, accept, multiple |

Layout

| Need | Component | Key Props | |------|-----------|-----------| | Content wrapper | Container | size, px, py | | Vertical stack | Stack | gap, align, justify | | Horizontal row | Group | gap, justify, wrap | | CSS Grid | Grid, GridCol | columns, gutter, span | | Full app shell | AppShell | header, navbar, aside, footer | | Card container | Card | shadow, padding, radius, withBorder | | Flex layout | Flex | direction, wrap, gap, align |

Navigation

| Need | Component | Key Props | |------|-----------|-----------| | Nav item | NavLink | label, href, active, leftSection | | Tabs | Tabs, TabsList, TabsPanel | value, orientation | | Breadcrumb | Breadcrumbs | separator | | Stepper | Stepper, StepperStep | active, onStepClick | | Pagination | Pagination | value, total, siblings | | Table of contents | TableOfContents | links, variant, active |

Feedback & Overlays

| Need | Component | Key Props | |------|-----------|-----------| | Modal dialog | Modal | opened, onClose, title, centered | | Side panel | Drawer | opened, onClose, position, size | | Toast | Notification | title, message, color, icon | | Alert banner | Alert | title, color, variant, icon | | Loading | Loader, LoadingOverlay | size, type, visible | | Progress | Progress, RingProgress | value, size, sections | | Tooltip | Tooltip | label, position, withArrow | | Copy button | CopyButton | value, timeout |

Data Display

| Need | Component | Key Props | |------|-----------|-----------| | Data table | Table | data, striped, highlightOnHover | | Accordion | Accordion, AccordionItem | value, multiple, variant | | Timeline | Timeline, TimelineItem | active, bulletSize | | Badge | Badge | color, variant, size |

Charts

| Need | Component | Key Props | |------|-----------|-----------| | Line | LineChart | data, dataKey, series | | Bar | BarChart | data, dataKey, series, orientation | | Area | AreaChart | data, dataKey, series | | Pie/Donut | DonutChart, PieChart | data, chartLabel | | Scatter | ScatterChart | data, dataKey, series |

What's New in Recent Versions

v2.5.x:

  • TableOfContents - Auto-generated table of contents from headings
  • selectFirstOptionOnDropdownOpen prop for Select/MultiSelect/Autocomplete
  • openOnFocus prop for Combobox components
  • AppShell mode="static" for nested shells
  • window.MantineCore / window.MantineHooks for custom component building

v2.4.x:

  • CopyButton / CustomCopyButton - Clipboard operations
  • getEditor(id) - Access RichTextEditor TipTap instance in clientside callbacks
  • Function props for chart axis/grid customization

v2.3.x:

  • MiniCalendar - Compact calendar component
  • ScrollAreaAutoheight - Auto-sizing scroll area
  • DirectionProvider - RTL text direction support

→ Full component reference: references/components-quick-ref.md


Core Patterns

Theming

Configure theme via MantineProvider:

theme = {
    "primaryColor": "blue",
    "fontFamily": "Inter, sans-serif",
    "defaultRadius": "md",
    "colors": {
        "brand": ["#f0f9ff", "#e0f2fe", "#bae6fd", "#7dd3fc", "#38bdf8",
                  "#0ea5e9", "#0284c7", "#0369a1", "#075985", "#0c4a6e"]
    },
    "components": {
        "Button": {"defaultProps": {"size": "md", "radius": "md"}},
        "TextInput": {"defaultProps": {"size": "sm"}},
    }
}

app.layout = dmc.MantineProvider(
    theme=theme,
    forceColorScheme="light",  # or "dark", or None for auto
    children=[...]
)

Theme Toggle Pattern (clientside callback):

from dash import clientside_callback, ClientsideFunction

app.layout = dmc.MantineProvider(
    id="mantine-provider",
    children=[
        dcc.Store(id="theme-store", storage_type="local", data="light"),
        dmc.Switch(id="theme-switch", label="Dark mode", checked=False),
        # ... rest of layout
    ]
)

clientside_callback(
    """(checked) => checked ? "dark" : "light" """,
    Output("mantine-provider", "forceColorScheme"),
    Input("theme-switch", "checked"),
)

→ Full theming guide: references/theming-patterns.md

Styling

Style Props - Universal props on all DMC components:

| Prop | CSS Property | Values | |------|--------------|--------| | m, mt, mb, ml, mr, mx, my | margin | xs, sm, md, lg, xl or number (px) | | p, pt, pb, pl, pr, px, py | padding | same as margin | | c | color | "blue", "red.6", "dimmed", "var(--mantine-color-text)" | | bg | background | same as color | | w, h | width, height | "100%", "50vw", number (px) | | maw, mah, miw, mih | max/min width/height | same as w, h | | fw | font-weight | 400, 500, 700 | | fz | font-size | xs, sm, md, lg, xl or number | | ta | text-align | "left", "center", "right" | | td | text-decoration | "underline", "line-through" |

Responsive Values - Dict with breakpoints:

dmc.Button("Click", w={"base": "100%", "sm": "auto", "lg": 200})
dmc.Stack(gap={"base": "xs", "md": "lg"})

Styles API - Target nested elements:

dmc.Select(
    data=["A", "B", "C"],
    classNames={"input": "my-input", "dropdown": "my-dropdown"},
    styles={"label": {"fontWeight": 700}, "input": {"borderColor": "blue"}},
)

→ Full styling guide: references/styling-guide.md

Callbacks

Basic Pattern:

from dash import callback, Input, Output, State

@callback(
    Output("output", "children"),
    Input("button", "n_clicks"),
    State("input", "value"),
    prevent_initial_call=True,
)
def update(n_clicks, value):
    return f"Clicked {n_clicks} times with value: {value}"

Pattern-Matching (dynamic components):

from dash import ALL, MATCH, callback_context as ctx

# ALL: Respond to any button with type "dynamic-btn"
@callback(
    Output("output", "children"),
    Input({"type": "dynamic-btn", "index": ALL}, "n_clicks"),
)
def handle_all(n_clicks_list):
    triggered = ctx.triggered_id  # {"type": "dynamic-btn", "index": X}
    return f"Button {triggered['index']} clicked"

# MATCH: Update the output matching the triggered input
@callback(
    Output({"type": "item-output", "index": MATCH}, "children"),
    Input({"type": "item-btn", "index": MATCH}, "n_clicks"),
    prevent_initial_call=True,
)
def handle_match(n):
    return f"Clicked {n} times"

Clientside Callback (browser-side JavaScript):

from dash import clientside_callback

clientside_callback(
    """(n) => n ? `Clicked ${n} times` : "Not clicked" """,
    Output("output", "children"),
    Input("button", "n_clicks"),
)

DMC-Specific Props:

  • debounce=300 - Delay callback trigger (ms) for TextInput, Textarea
  • persistence=True - Persist value across page reloads
  • persistence_type="local" - Storage type: memory, local, session

→ Full callbacks reference: references/callbacks-advanced.md


Multi-Page Apps

Use Dash Pages with DMC AppShell:

# app.py
import dash
from dash import Dash
import dash_mantine_components as dmc

app = Dash(__name__, use_pages=True, pages_folder="pages")

app.layout = dmc.MantineProvider([
    dmc.AppShell(
        [
            dmc.AppShellHeader(dmc.Group([
                dmc.Title("My App", order=3),
                dmc.Switch(id="theme-switch"),
            ], h="100%", px="md")),
            dmc.AppShellNavbar([
                dmc.NavLink(label=page["name"], href=page["path"], active=page["path"] == "/")
                for page in dash.page_registry.values()
            ], p="md"),
            dmc.AppShellMain(dash.page_container),
        ],
        header={"height": 60},
        navbar={"width": 250, "breakpoint": "sm", "collapsed": {"mobile": True}},
        padding="md",
    )
])

if __name__ == "__main__":
    app.run(debug=True)
# pages/home.py
import dash
import dash_mantine_components as dmc

dash.register_page(__name__, path="/", name="Home")

layout = dmc.Container([
    dmc.Title("Welcome", order=2),
    dmc.Text("Home page content"),
], py="xl")
# pages/analytics.py
import dash
import dash_mantine_components as dmc

dash.register_page(__name__, path="/analytics", name="Analytics")

layout = dmc.Container([
    dmc.Title("Analytics", order=2),
    # Charts, tables, etc.
], py="xl")

Variable Paths:

# pages/user.py
dash.register_page(__name__, path_template="/user/<user_id>")

def layout(user_id=None):
    return dmc.Container([
        dmc.Title(f"User: {user_id}", order=2),
    ])

→ Full multi-page guide: references/multi-page-apps.md


Component Categories

Quick links to reference documentation:

| Category | Components | Reference | |----------|------------|-----------| | All Components | 90+ components with props/events | components-quick-ref.md | | Theming | MantineProvider, theme object, colors | theming-patterns.md | | Styling | Style props, Styles API, CSS variables | styling-guide.md | | Callbacks | Pattern-matching, clientside, background | callbacks-advanced.md | | Multi-Page | Dash Pages, routing, AppShell | multi-page-apps.md | | Charts | Data formats, series config | charts-data-formats.md | | Date Pickers | DatePicker, DatesProvider, localization | date-pickers-guide.md | | Dash Core | dcc.Store, caching, performance | dash-fundamentals.md | | Migration | v1.x to v2.x breaking changes | migration-v2.md |

Asset Templates

Copy and adapt these templates:

| Template | Description | |----------|-------------| | app_single_page.py | Complete single-page DMC app with theme toggle | | app_multi_page.py | Multi-page app with Dash Pages and AppShell | | callbacks_patterns.py | All callback pattern examples | | theme_presets.py | Pre-built theme configurations |

Utility Scripts

| Script | Usage | |--------|-------| | fetch_docs.py | python fetch_docs.py "Select" - Fetch/search official llms.txt | | scaffold_app.py | python scaffold_app.py myapp --type multi --shell | | generate_theme.py | python generate_theme.py --primary "#0ea5e9" | | component_search.py | python component_search.py "select" |


Common Tasks

Form with Validation

@callback(
    Output("submit-btn", "disabled"),
    Output("error-text", "children"),
    Input("email-input", "value"),
    Input("password-input", "value"),
)
def validate_form(email, password):
    errors = []
    if not email or "@" not in email:
        errors.append("Valid email required")
    if not password or len(password) < 8:
        errors.append("Password must be 8+ characters")
    return bool(errors), ", ".join(errors)

Modal Open/Close

app.layout = dmc.MantineProvider([
    dmc.Button("Open Modal", id="open-modal-btn"),
    dmc.Modal(
        id="my-modal",
        title="Confirm Action",
        children=[
            dmc.Text("Are you sure?"),
            dmc.Group([
                dmc.Button("Cancel", id="cancel-btn", variant="outline"),
                dmc.Button("Confirm", id="confirm-btn", color="red"),
            ], justify="flex-end", mt="md"),
        ],
    ),
])

@callback(
    Output("my-modal", "opened"),
    Input("open-modal-btn", "n_clicks"),
    Input("cancel-btn", "n_clicks"),
    Input("confirm-btn", "n_clicks"),
    prevent_initial_call=True,
)
def toggle_modal(open_clicks, cancel, confirm):
    from dash import ctx
    if ctx.triggered_id == "open-modal-btn":
        return True
    return False

Loading State

from dash import dcc

app.layout = dmc.MantineProvider([
    dmc.Button("Load Data", id="load-btn"),
    dcc.Loading(
        id="loading",
        type="circle",
        children=dmc.Container(id="data-container"),
    ),
])

@callback(Output("data-container", "children"), Input("load-btn", "n_clicks"))
def load_data(n):
    import time
    time.sleep(2)  # Simulate slow operation
    return dmc.Text("Data loaded!")

Chart with Data

data = [
    {"month": "Jan", "sales": 100, "profit": 20},
    {"month": "Feb", "sales": 150, "profit": 35},
    {"month": "Mar", "sales": 120, "profit": 25},
]

dmc.BarChart(
    data=data,
    dataKey="month",
    series=[
        {"name": "sales", "color": "blue.6"},
        {"name": "profit", "color": "green.6"},
    ],
    h=300,
    withLegend=True,
    withTooltip=True,
)

Troubleshooting

Common Errors

| Error | Cause | Fix | |-------|-------|-----| | MantineProvider is required | Component outside provider | Wrap entire layout in dmc.MantineProvider([...]) | | Invalid theme color | Color not in theme | Use built-in colors (blue, red) or add to theme["colors"] | | Callback output not found | Component not in layout | Ensure component with ID exists in layout | | Circular callback detected | Output also used as Input | Use State instead of Input for non-triggering values | | Pattern-matching ID mismatch | Dict keys don't match | Ensure type and index keys match exactly | | Duplicate callback outputs | Same output in multiple callbacks | Add allow_duplicate=True to additional callbacks |

Debug Tips

  1. Check browser console for JavaScript errors
  2. Use debug=True in app.run() for detailed Python errors
  3. Print ctx.triggered_id to see which input fired
  4. Validate JSON-serializable callback returns (no Python objects)
  5. Test with prevent_initial_call=True to avoid startup errors

DMC v2.x Gotchas

  • DateTimePicker: Use timePickerProps not timeInputProps
  • Carousel: Embla options need {"containScroll": "trimSnaps"} wrapper
  • Default reuseTargetNode=True may cause Portal issues - set to False if overlays misbehave
  • Use MantineProvider not MantineProviderV2 (deprecated)

→ Full migration guide: references/migration-v2.md