📣 Dash Mantine Components 2.4.0 Release

:sparkles: What’s New

  • New CopyButton and CustomCopyButton components — themed, flexible, and customizable copy-to-clipboard tools.
  • Chart components now support functions for props like xAxisProps and yAxisProps , making it easier to add dynamic, customized features to figures.
  • RichTextEditor adds code highlighting, new props (editable , focus), and full clientside editor API access.
  • AI-friendly docs — generate a custom llms.txt for your project and use “Copy for LLMs” on each docs page for faster, smarter AI-assisted development.

:play_button: Want to Try It Live?

This post shows screenshots only — for live interactive examples with code, check out the
:open_book: DMC 2.4.0 Release Announcement on the docs site.


New: Copy to Clipboard Components

The new CopyButton and CustomCopyButton components are similar to dcc.Clipboard , but with full Mantine styling and customization.

  • Customizable icons, colors, and labels for the copied state
  • Support for copying from another component using target_id
  • Trigger copying in callbacks with triggerCopy=True
  • Build fully custom copy-to-clipboard buttons with CustomCopyButton

Check out the new Copy Button docs.

Example 1: Styling the CopyButton

dmc240copybutton1

Here's the code
import dash_mantine_components as dmc
from dash_iconify import DashIconify

component = dmc.Group([
    dmc.CopyButton(
        value="https://www.dash-mantine-components.com/",
        children="Copy URL",
        copiedChildren="Copied!",
        color="blue",
        copiedColor="teal"
    ),
    dmc.CopyButton(
        value="This text is copied",
        children=DashIconify(icon="tabler:clipboard"),
        copiedChildren=DashIconify(icon="tabler:check"),
        color="blue",
        copiedColor="teal",
        variant="outline"
    ),
    dmc.CopyButton(
        value="This text is copied",
        children=DashIconify(icon="fa-regular:copy"),
        copiedChildren=DashIconify(icon="fa-regular:check-circle"),
        color="gray",
        copiedColor="dark",
        variant="transparent"
    )
])

Example 2: Copy from another component:

Need a quick way to say “No”? Fetch a random excuse from No-as-a-Service and copy it with a single click.

.
dmc240copybutton2

Here's the code
dmc.Box([
    dmc.Button("Get rejection reason", id="new-reason-btn", mb="lg"),
    dmc.Group([
        dmc.Text(id="reason-text", mt=10),
        dmc.CopyButton(
            target_id="reason-text",
            children=DashIconify(icon="fa-regular:copy"),
            copiedChildren=DashIconify(icon="fa-regular:check-circle"),
            color="blue",
            copiedColor="teal",
            variant="outline",
            size="xs"
        )
    ], align="flex-start",  wrap="nowrap",)
])


More chart props now support functions

The AreaChart , BarChart , BubbleChart , CompositeChart , LineChart , and ScatterChart now accept functions for these props:

  • xAxisProps, yAxisProps, gridProps, rightYAxisProps (all charts)
  • zAxisProps (BubbleChart only)

See the Functions As Props guide for details.

Example: format x-axis tick labels

This example uses a JavaScript function to format x-axis tick labels for datetime values.

here's the code

import dash_mantine_components as dmc
from dash import Dash
from datetime import datetime

data = [
  {"date": datetime(2025, 3, 22), "Apples": 2890, "Oranges": 2338, "Tomatoes": 2452},
  {"date": datetime(2025, 3, 23), "Apples": 2756, "Oranges": 2103, "Tomatoes": 2402},
  {"date": datetime(2025, 3, 24), "Apples": 3322, "Oranges": 986, "Tomatoes": 1821},
  {"date": datetime(2025, 3, 25), "Apples": 3470, "Oranges": 2108, "Tomatoes": 2809},
  {"date": datetime(2025, 3, 26), "Apples": 3129, "Oranges": 1726, "Tomatoes": 2290}
]

component = dmc.AreaChart(
    h=300,
    dataKey="date",
    data=data,
    series = [
        {"name": "Apples", "color": "indigo.6"},
        {"name": "Oranges", "color": "blue.6"},
        {"name": "Tomatoes", "color": "teal.6"}
    ],
    curveType="Monotone",
    tickLine="xy",
    withGradient=False,
    withDots=False,
    xAxisProps={"tickFormatter": {"function": "formatDatetime"}},
    valueFormatter={"function": "formatNumberIntl"},
)

var dmcfuncs = window.dashMantineFunctions = window.dashMantineFunctions || {};

dmcfuncs.formatDatetime = function (datetimeStr) {
  const date = new Date(datetimeStr);
  return date.toLocaleDateString('en-US', {
    month: 'short',
    day: 'numeric',
    year: 'numeric'
  });
};


New RichTextEditor features

  • Code highlighting is now available
  • New Props: editable, focus Thanks for the PR @chgiesse
  • Access Editor API in a clientside callback

See examples of all the new features in the RichTextEditor docs

Example: Editable prop

Note the toolbar is hidden when the editor is read-only

dmc240rte1

here's the code
import dash_mantine_components as dmc
from dash import  Input, Output, callback

component = dmc.Box([
    dmc.Switch(
        id="rte-toggle-editable",
        label="Editable",
        checked=True,
    ),
    dmc.RichTextEditor(
        id="rte-editable",
        html="<p>This editor can be toggled between editable and read-only mode.</p>",
        editable=True,
        toolbar={
            "controlsGroups": [
                [
                    "Bold",
                    "Italic",
                    "Underline",
                    "Strikethrough",
                    "CodeBlock"
                ],
            ],
        },
    ),
])

@callback(
    Output("rte-editable", "editable"),
    Input("rte-toggle-editable", "checked"),
)
def toggle_editable(checked):
    return checked

Example: Focus prop:

Use the focus prop to control the editor’s focus state.

  • focus=True - Focus the editor at the current cursor position
  • focus=False - Blur (remove focus from) the editor
  • focus="start" - Focus at the start of the document
  • focus="end" - Focus at the end of the document
  • focus=10 - Focus at a specific position (character offset) Positive values start at the beginning of the document - negative values at the end.
  • focus="all" - Focus and select all content

Example: Code Highlighting in RichTextEditor

Example: Accessing Editor clientside

Get full access to the editor API in clientside callbacks, including executing commands, inspecting content, and updating the editor state.

Here's the code
import dash_mantine_components as dmc
from dash import Dash, Input, Output, clientside_callback

component = dmc.Box([
    dmc.RichTextEditor(
        id="get-editor-id",
        toolbar={
                "controlsGroups": [
                    [
                        "Bold",
                        "Italic",
                        "Underline",
                        "Strikethrough",
                    ],
                ],
            },
        html="<p>Try typing some text in this editor. Click the button below to see your character and word count.</p>"
    ),
    dmc.Button("Get Stats", id="btn-rte-stats", n_clicks=0),
    dmc.Box(id="stats"),
])

clientside_callback(
    """
    function(n_clicks) {
        if (n_clicks > 0) {
            const editor = dash_mantine_components.getEditor('get-editor-id');
            if (editor) {
                const text = editor.getText();
                const chars = text.length;
                const words = text.split(/\\s+/).filter(Boolean).length;
                return `Characters: ${chars} | Words: ${words}`;
            }
        }
        return dash_clientside.no_update;
    }
    """,
    Output("stats", "children"),
    Input("btn-rte-stats", "n_clicks"),
)

AI-friendly documentation

LLM-friendly documentation is now available for Dash Mantine Components. It follows the llms.txt standard, enabling AI tools like ChatGPT, Cursor, Windsurf, and Claude to better understand DMC components and props.

New in this release:

  • Generate a custom llms.txt file that includes only the components you use — improving AI accuracy and response speed.
  • Each docs page now includes a “Copy for LLMs” button for copying AI-friendly content directly into a chat.

See the LLMs section for full details and customization options.

Other notable updates

  • Mantine patch updates from 8.3.1 through 8.3.6. See the Mantine Releases for details.
  • Improved rendering in several components, including the Stepper. Thanks to @chgiesse for PR #664.

:raising_hands: Thank You

Special thanks to:

  • @chgiesse for contributing two PRs in this release
  • @BSd3v for providing help and guidance along the way
  • @alexcjohnson for thoughtful feedback during code reviews
4 Likes

Great work!

Regarding the functions as props: exciting to see these examples, super nice! Is this a first step so that we could implement stuff from the upstream combobox examples? That would be super awesome!

Hi @datenzauberai

I had the same thought while working on the CopyButton and the CustomCopyButton. The CopyButton is easy to use. You can style it simply by using props - no JavaScript required. For advanced users who want full customization, the CustomCopyButton gives you access to Mantine’s copy hook, and you can build whatever you like.

I think we can do something similar with the Select (Combobox) components. It would allow you to build custom select components like the Mantine custome examples.

Ideally, I’d prefer to get the extension template working, but I’ve run into issues as described in GitHub issue #641

Pull requests for either solution are welcome :slight_smile:

Supercharging Your Dash Apps with DMC 2.4.0: CopyButton, CustomCopyButton, and “Copy for LLMs”

Now that you’ve seen what’s new in DMC 2.4.0, let’s take a closer look at how to use the two new components: CopyButton and CustomCopyButton.

This post focuses on a workflow using the new “Copy for LLMs” feature on each documentation page to help AI understand the latest DMC features, including advanced ones like Functions as Props. You can then apply this to the other new features announced above.


Why Use DMC’s Copy Buttons?

If you’ve ever needed to copy text to the clipboard in a Dash app, you may have used dcc.Clipboard.
It works — but the new DMC copy buttons are more flexible. They are fully themeable using Mantine’s system (colors, variants, sizes, etc.), plus they are highly customizable.

Let’s start simple.

See it live on PyCafe: PyCafe - Dash - CopyButton simple example


The Basic CopyButton

When clicked, this button copies the URL, briefly changes its label to “Copied!”, turns teal, then resets itself — all automatically.

import dash
import dash_mantine_components as dmc

app = dash.Dash()

app.layout = dmc.MantineProvider(
    dmc.Container([
        dmc.Title("Check Out the New CopyButton!", order=2, mt="xl"),
        dmc.Text("See the CopyButton documentation at https://www.dash-mantine-components.com/components/copybutton", mt="md"),
        dmc.CopyButton(
            value="https://www.dash-mantine-components.com/components/copybutton",
            children="Copy URL",
            copiedChildren="Copied!",
            color="blue",
            copiedColor="teal",
            mt="md"
        ),       
    ])
)

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

What Makes CopyButton Great

The CopyButton has a lot of flexibility:

  • Mantine styling: Supports all standard Button props (e.g., variant, size, color)
  • Dynamic appearance: Use copiedColor and copiedChildren props to change the look after copying
  • Copy from another component: Use target_id to grab text from another element - no callback required!
  • Trigger copy from a callback: Set triggerCopy=True inside a callback to copy
  • Adjust timing: Control the copied state duration with the timeout prop (default 1000 ms)

:light_bulb: Tip: AI assistants like ChatGPT or Claude aren’t trained on these new features yet.
To help them understand, go to the CopyButton documentation, click “Copy for LLMs”, and paste that Markdown version into your chat window before asking questions.


Here’s another quick example — copying an API key, no callback required.
The button automatically copies whatever text is displayed in the target component.

See it live on PyCafe: PyCafe - Dash - CopyButton demo - copy text from another component

from dash import  Dash, Output, Input
import dash_mantine_components as dmc
from dash_iconify import DashIconify
import secrets

app = Dash()

app.layout = dmc.MantineProvider(
    dmc.Container([
        dmc.Title("CopyButton Demo", order=3, mb="md"),
        dmc.Button("Generate Key", id="generate-btn"),
        dmc.Group([
            dmc.Text(id="key-display"),
            dmc.CopyButton(
                target_id="key-display",
                children=DashIconify(icon="fa-regular:copy"),
                copiedChildren=DashIconify(icon="fa-regular:check-circle"),
                color="gray",
                copiedColor="teal",
                variant="outline",
                size="compact-md"
            ),
        ], mt="lg")
    ])
)


@app.callback(
    Output("key-display", "children"),
    Input("generate-btn", "n_clicks"),
)
def generate_key(_):
    return secrets.token_urlsafe(16)


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

CustomCopyButton: Full Customization, Powered by JavaScript

Sometimes, a button isn’t quite the right fit. Maybe you want a subtle icon with a tooltip — something that feels more like part of your UI than a separate control.

That’s what CustomCopyButton is for.

It uses Functions as Props, meaning you can define a JavaScript function that returns any React element you want — with full access to the copy state (copied) and handler (copy).


Step 1: Let AI Write the JavaScript

Here’s the best part: you don’t have to code the JavaScript yourself.

Just open the Functions as Props docs, click “Copy for LLMs”, and paste them into Claude or ChatGPT with a simple prompt like this:

Create a chat-style component with:

  • A Textarea and a CustomCopyButton icon in the corner
  • Use an ActionIcon with a tooltip that says “Copy content” or “Copied!”
  • The tooltip should use the target prop
  • The icon and color should switch between “tabler:copy” (gray) and “tabler:check” (teal)

The model should generate the correct JavaScript function — because it now understands DMC’s internal patterns from the LLMs file.

:light_bulb: Tip: IWhen prompting AI to write JavaScript for DMC, describe what you want in clear, code-like language. If the output isn’t quite right, write the function in Python first and then ask the AI to translate it to JavaScript. It usually gives much better results.


Run this example in Pycafe: PyCafe - Dash - Demo of CustomCopyButton in Textarea


Add the JavaScript to a .js file in the /assets folder:

var dmcfuncs = window.dashMantineFunctions = window.dashMantineFunctions || {};
var dmc = window.dash_mantine_components;
var DashIconify = window.dash_iconify.DashIconify;

dmcfuncs.chatCopyIcon = function({ copied, copy }) {
  return [
    React.createElement(
      dmc.ActionIcon,
      {
        key: 'icon',
        id: 'chat-copy-icon',
        color: copied ? 'teal' : 'gray',
        variant: 'subtle',
        onClick: copy,
        style: { position: 'absolute', top: -10, right: -10}
      },
      React.createElement(DashIconify, {
        icon: copied ? 'tabler:check' : 'tabler:copy',
        style: { width: 16, height: 16 }
      })
    ),
    React.createElement(
      dmc.Tooltip,
      {
        key: 'tooltip',
        target: '#chat-copy-icon',
        label: copied ? 'Copied!' : 'Copy content',
        withArrow: true,
        position: 'left'
      }
    )
  ];
};

And here’s how to use it in the Dash app (Note - the AI generated this Dash app too!):

import dash_mantine_components as dmc
from dash import Dash, Input, Output
from dash_iconify import DashIconify  # Required for chatCopyIcon

app = Dash()

app.layout = dmc.MantineProvider(
    dmc.Paper(
        [
            dmc.Box(
                [
                    dmc.Textarea(
                        id="chat-textarea",
                        value="This could be a chat message or generated text.",
                        autosize=True,
                        minRows=4,
                        variant="unstyled",
                    ),
                    dmc.CustomCopyButton(
                        value="",
                        children={"function": "chatCopyIcon"},
                        id="copy-icon"
                    ),
                ],
                style={"position": "relative"},
            )
        ],
        shadow="lg",
        radius="xl",
        m="xl",
        p="sm",
        withBorder=True,
        style={
            "position": "relative",
            "maxWidth": 600,
        },
    )
)


@app.callback(
    Output("copy-icon", "value"),
    Input("chat-textarea", "value"),
)
def update(v):
    return v

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

Here’s a tighter, smoother rewrite of that section — incorporating your note about browser-based vs IDE-based AI assistants:


Why Use “Copy for LLMs”?

The new Copy for LLMs” button on every DMC documentation page is helpful for fast-moving libraries like DMC when AI models are not trained on the latest version. It’s especially useful for free, web-based assistants like ChatGPT or Claude — just copy the Markdown from the docs and paste it into your chat for accurate, up-to-date help.

If you’re using an IDE-integrated AI tool (like Cursor or Windsurf), check out the LLMs section in the docs — you can download the complete docs or select multiple pages at once for offline use.

Wrapping Up

DMC 2.4.0 isn’t just about new components — it’s a new way to work with AI tools. By using the “Copy for LLMs” button, you can quickly get accurate help and examples for any feature in the docs.

Try it with other new features in this release too:

  • In RichTextEditor, use the new focus and editable props without writing JavaScript, or use Functions as Props to access the Tiptap editor instance and unlock advanced features.
  • In charts, pass JavaScript functions directly to Recharts figures for custom behavior.

Once you see how it works, you can apply the same workflow anywhere in DMC to learn, customize, and experiment faster.

I was out of pocket for a few days, and went to dmc docs this morning for another thing. You should have heard my “yessss” when I saw 2.4.0 release in the navbar. Thanks for the continued improvements, every time there is a release I improve my apps!

2 Likes