## RichTextEditor A TipTab based rich text editor . Category: Inputs ### Tiptap editor The `RichTextEditor` component is built on top of the [Tiptap editor](https://tiptap.dev/api/editor) For more information see the documentation on [tiptap.dev](https://tiptap.dev) website. Tiptap version note: - DMC 2.4.1 uses Tiptap v3.14.0. - DMC 2.3.0–2.4.0 use Tiptap v3.3. There are no known breaking changes; however, review the migration guide for details. - DMC versions prior to 2.3.0 use Tiptap v2.9. ```python import dash_mantine_components as dmc content = """
RichTextEditor component focuses on usability and is designed to be as simple as possible to bring a familiar editing experience to regular users. RichTextEditor is based on Tiptap.dev and supports all of its features:
| Name | Description | ||
|---|---|---|---|
| Cyndi Lauper | Singer | Songwriter | Actress |
| Bruce Springsteen | Singer | Songwriter | Actor |
This is a basic example of implementing images. Drag to re-order.
Regular paragraph
{code_example}",
extensions=[
{"StarterKit": { "codeBlock": False }},
"CodeBlockLowlight"
],
toolbar={
"controlsGroups": [
[
"Bold",
"Italic",
"Underline",
"Strikethrough",
"CodeBlock"
],
],
},
)
```
### Source code mode
You can use the `SourceCode` control to see and edit source code of editor content:
```python
import dash_mantine_components as dmc
content= 'Source code control example
New line with bold text
New line with italic text
' component =dmc.RichTextEditor( html=content, toolbar={ "sticky": True, "controlsGroups": [ ["SourceCode"], [ "Blockquote", "Bold", "Italic", "Underline", "Strikethrough", "ClearFormatting", "Highlight", ], ], }, ) ``` ### Focus *New in V2.4.0* 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:** ```python import dash_mantine_components as dmc from dash import Input, Output, callback, ctx, no_update component = dmc.Box([ dmc.Group([ dmc.Button("Focus Start", id="rte-btn-focus-start"), dmc.Button("Focus End", id="rte-btn-focus-end"), dmc.Button("Blur", id="rte-btn-blur"), ]), dmc.RichTextEditor( id="rte-focus", html="Click the buttons to control focus.
", ), ]) @callback( Output("rte-focus", "focus"), Input("rte-btn-focus-start", "n_clicks"), Input("rte-btn-focus-end", "n_clicks"), Input("rte-btn-blur", "n_clicks"), prevent_initial_call=True ) def control_focus(start, end, blur): if not ctx.triggered: return no_update button_id = ctx.triggered_id if button_id == "rte-btn-focus-start": return "start" elif button_id == "rte-btn-focus-end": return "end" elif button_id == "rte-btn-blur": return False ``` ### Editable *New in V2.4.0* The `editable` prop controls whether the editor content can be modified. When `editable=False`: - The editor becomes read-only - Users can still select and copy text - The toolbar is automatically hidden ```python 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="This editor can be toggled between editable and read-only mode.
", 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 ``` ### Sticky toolbar Set `sticky` prop on `RichTextEditor` `toolbar` prop to make toolbar sticky, control top property with `stickyOffset`. For example, in the dmc docs website there is a header with 60px height, in this case we will need to set `stickyOffset=60` to make sticky position correctly with fixed positioned header element. Note the sticky toolbar as you scroll past the example below. ### Subtle Variant `variant="subtle"` removes borders from the controls groups, makes controls larger and reduces spacing of the toolbar: ```python import dash_mantine_components as dmc component = dmc.RichTextEditor( html="Subtle rich text editor variant", extensions=[ "StarterKit", "Highlight", ], toolbar={ "sticky": True, "stickyOffset": 60, "variant": "subtle", "controlsGroups": [ [ "Bold", "Italic", "Underline", "Strikethrough", "ClearFormatting", "Highlight", "Code", ], ], }, ) ``` ### Labels and localization `RichTextEditor` supports changing labels for all controls with labels prop: ```python import dash_mantine_components as dmc colorpicker_colors = [ "#25262b", "#868e96", "#fa5252", "#e64980", "#be4bdb", "#7950f2", "#4c6ef5", ] component = dmc.RichTextEditor( html="Custom button labels", toolbar={ "sticky": True, "stickyOffset": 60, "controlsGroups": [ [ "Bold", "Italic", "Underline", "Strikethrough", "ClearFormatting", "Highlight", "Code", ], [{"ColorPicker": {"colors": colorpicker_colors}}], [ {"Color": {"color": "red"}}, {"Color": {"color": "green"}}, {"Color": {"color": "blue"}}, ], ["UnsetColor"], ], }, labels={ "boldControlLabel": "Make text bold", "italicControlLabel": "Make text bold", "colorPickerControlLabel": "Text color", # label for control in toolbar "colorPickerColorLabel": "Color number: {color}", # include color in label in the color swatch. Use f-string format "colorControlLabel": "Set Text color {color}" # include color in label with f-string format # ...other labels }, ) ``` Most labels are used to add `aria-label` and `title` attributes to the toolbar controls. Some labels support f-string formatting for dynamic values. If you do not provide all labels, then they will be merged with the default labels. Here are all available labels with their defaults: ```python default_labels = { # Controls labels "linkControlLabel": "Link", "colorPickerControlLabel": "Text color", "highlightControlLabel": "Highlight text", "colorControlLabel": "Set text color {color}", # Use f-string format to include color in label "boldControlLabel": "Bold", "italicControlLabel": "Italic", "underlineControlLabel": "Underline", "strikeControlLabel": "Strikethrough", "clearFormattingControlLabel": "Clear formatting", "unlinkControlLabel": "Remove link", "bulletListControlLabel": "Bullet list", "orderedListControlLabel": "Ordered list", "h1ControlLabel": "Heading 1", "h2ControlLabel": "Heading 2", "h3ControlLabel": "Heading 3", "h4ControlLabel": "Heading 4", "h5ControlLabel": "Heading 5", "h6ControlLabel": "Heading 6", "blockquoteControlLabel": "Blockquote", "alignLeftControlLabel": "Align text: left", "alignCenterControlLabel": "Align text: center", "alignRightControlLabel": "Align text: right", "alignJustifyControlLabel": "Align text: justify", "codeControlLabel": "Code", "codeBlockControlLabel": "Code block", "subscriptControlLabel": "Subscript", "superscriptControlLabel": "Superscript", "unsetColorControlLabel": "Unset color", "hrControlLabel": "Horizontal line", "undoControlLabel": "Undo", "redoControlLabel": "Redo", # Task list "tasksControlLabel": "Task list", "tasksSinkLabel": "Decrease task level", "tasksLiftLabel": "Increase task level", # Link editor "linkEditorInputLabel": "Enter URL", "linkEditorInputPlaceholder": "https://example.com/", "linkEditorExternalLink": "Open link in a new tab", "linkEditorInternalLink": "Open link in the same tab", "linkEditorSave": "Save", # Color picker control "colorPickerCancel": "Cancel", "colorPickerClear": "Clear color", "colorPickerColorPicker": "Color picker", "colorPickerPalette": "Color palette", "colorPickerSave": "Save", "colorPickerColorLabel": "Set Text color {color}", # Use f-string format to include color in color swatch label } ``` ### JSON or HTML Content The editor supports content in either [JSON (ProseMirror) or HTML format](https://tiptap.dev/docs/editor/core-concepts/schema). You can specify the format using the `json` or `html` prop. If both props are set, `json` takes precedence. Note: While users can type Markdown-style text into `RichTextEditor`, the component does not parse or render supplied text in Markdown content. To render Markdown text, use the `dcc.Markdown` component instead. #### When to Use Each Format: - **JSON (ProseMirror)**: Ideal for structured data storage (databases, APIs) or programmatic content manipulation (e.g., dynamically adding elements). - **HTML**: Useful for direct rendering in a browser, email clients, or using with components like `dcc.Markdown`. Note that the schema is very strict. For example, if you use `This is important`, but don’t have any [extension](/components/richtexteditor#tiptap-extensions) that handles strong tags, you’ll only see `This is important` – without the bold formatting.. For details on the schema and ProseMirror format, see the [Tiptap documentation](https://tiptap.dev/docs/editor/core-concepts/schema). Try editing the content in this example to see the JSON and HTML format: ```python from dash import Input, Output, html, callback import dash_mantine_components as dmc content = """Change font size with the controls!
", toolbar={ "controlsGroups": [ ["H1", "H2", "H3", "H4"], [ { "CustomControl": { "aria-label": "Increase font size", "title": "Increase font size", "children": DashIconify(icon="mdi:format-font-size-increase", width=16), "onClick": {"function": "increaseFontSize"}, }, }, { "CustomControl": { "aria-label": "Decrease font size", "title": "Decrease font size", "children": DashIconify(icon="mdi:format-font-size-decrease", width=16), "onClick": {"function": "decreaseFontSize"}, }, }, ], ], }, ) ``` ```javascript var dmcfuncs = window.dashMantineFunctions = window.dashMantineFunctions || {}; function changeFontSize(editor, delta) { if (!editor) return; const { from, to } = editor.state.selection; let size = 16; // default editor.state.doc.nodesBetween(from, to, (node) => { if (node.isText) { const mark = node.marks.find(m => m.type.name === "textStyle"); if (mark?.attrs.fontSize) { size = parseInt(mark.attrs.fontSize, 10); } } }); const newSize = Math.min(Math.max(size + delta, 8), 72) + "px"; editor.chain().focus().setFontSize(newSize).run(); } dmcfuncs.increaseFontSize = ({ editor }) => changeFontSize(editor, 2); dmcfuncs.decreaseFontSize = ({ editor }) => changeFontSize(editor, -2); ``` ### Selected text The `selected` prop contains the currently selected text. Note that it is text only and does not include any formatting. ```python from dash import Input, Output, html, callback import dash_mantine_components as dmc component = html.Div( [ dmc.RichTextEditor( html="Welcome to the editor.
Select some text
", extensions=[ "StarterKit", ], toolbar={ "controlsGroups": [ [ "Bold", "Italic", "Underline", "Strikethrough", "Highlight", ], ] }, id="rte", ), dmc.Box(id="rte-selected", mt="lg"), ] ) @callback(Output("rte-selected", "children"), Input("rte", "selected")) def update_content(content: str): return f"Your selected text: {content}" ``` ### Accessing the Editor Instance clientside The `dash_mantine_components.getEditor(id)` function provides direct access to the underlying Tiptap editor instance in clientside callbacks. This allows you full access to the editor API including executing commands, inspecting content, and updating the editor state. See the [Tiptap editor API](https://tiptap.dev/docs/editor/api/commands) for more details. This returns the Tiptap editor instance for the specified component ID, or `undefined` if the editor doesn't exist: ```javascript const editor = dash_mantine_components.getEditor('editor-id'); ``` This example shows how to access the editor in a clientside callback and provide a word count of the content. ```python 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="Try typing some text in this editor. Click the button below to see your character and word count.
" ), 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"), ) ``` ### Debounce The `debounce` prop controls how updates to the `html`, `json`, and `selected` props are triggered in the `RichTextEditor`. Enabling debounce helps prevent excessive callbacks by delaying updates until the user stops interacting. If set to `True`, updates occur only when the editor loses focus. Alternatively, you can specify a delay in milliseconds to fine-tune when updates should be sent. ```python dmc.RichTextEditor( debounce=500 # # Delay updates by 500ms ) ``` ### Persistence Dash provides built-in persistence props that allow components to retain their values across page reloads or app sessions. In `RichTextEditor`, this means the editor content can be saved and restored automatically. The following persistence-related props are available: - `persistence` – Enables persistence (`True`, `"local"`, `"session"`, or `"memory"`). - `persisted_props` – Specifies which props should be persisted. By default it's `["html, json"]` - `persistence_type` – Defines where the data is stored (`"local"`, `"session"`, or `"memory"`). For more details on how Dash handles persistence, refer to the [Dash persistence documentation](https://dash.plotly.com/persistence). Notes: - The component must have an `id` for persistence to work. - If you want to set an initial value while also enabling persistence, set `persisted_props` to only the prop used for the initial value. For example `persisted_props=['html']`. ### CSS Extensions As of DMC 1.2.0, RichTextEditor component styles are bundled automatically, so you no longer need to include a separate CSS file. If you're using an older version of DMC, refer to the [migration guide](/migration) for instructions on including optional stylesheets. ### Styles API This component supports Styles API. With Styles API, you can customize styles of any inner element. See the Styling and Theming sections of these docs for more information. #### RichTextEditor Selectors | Selector | Static Selector | Description | |----------------------------------|------------------------------------------------------|-------------| | `root` | `.mantine-RichTextEditor-root` | Root element | | `toolbar` | `.mantine-RichTextEditor-toolbar` | Toolbar element | | `content` | `.mantine-RichTextEditor-content` | Content area | | `typographyStylesProvider` | `.mantine-RichTextEditor-typographyStylesProvider` | TypographyStylesProvider component, wraps content | | `control` | `.mantine-RichTextEditor-control` | `RichTextEditor.Control` root element, used as a base for all controls | | `controlIcon` | `.mantine-RichTextEditor-controlIcon` | Control icon element | | `controlsGroup` | `.mantine-RichTextEditor-controlsGroup` | `RichTextEditor.ControlsGroup` component root | | `linkEditor` | `.mantine-RichTextEditor-linkEditor` | Link editor root element | | `linkEditorSave` | `.mantine-RichTextEditor-linkEditorSave` | Link editor save button | | `linkEditorInput` | `.mantine-RichTextEditor-linkEditorInput` | Link editor URL input | | `linkEditorExternalControl` | `.mantine-RichTextEditor-linkEditorExternalControl` | Link editor external button | | `linkEditorDropdown` | `.mantine-RichTextEditor-linkEditorDropdown` | Link editor popover dropdown element | #### RichTextEditor Data Attributes | Selector | Attribute | Condition | |-----------|--------------|---------------------------| | `control` | `data-active` | Control is active | ### Keyword Arguments #### RichTextEditor - id (string; optional): Unique ID to identify this component in Dash callbacks. - aria-* (string; optional): Wild card aria attributes. - attributes (boolean | number | string | dict | list; optional): Passes attributes to inner elements of a component. See Styles API docs. - className (string; optional): Class added to the root element, if applicable. - classNames (dict; optional): Adds custom CSS class names to inner elements of a component. See Styles API docs. - darkHidden (boolean; optional): Determines whether component should be hidden in dark color scheme with `display: none`. - data-* (string; optional): Wild card data attributes. - debounce (number | boolean; optional): If True, changes will be sent back to Dash only when losing focus. If False, data will be sent on every change. If a number, data will be sent when the value has been stable for that number of milliseconds. - editable (boolean; optional): If True, the editor will be editable. True by default. - extensions (list; optional): List of extensions to be loaded by the editor. Each item can be either a string with the extension name (e.g. 'Color') or an object with the extension name as key and options as value (e.g. {'TextAlign': {'types': ['heading', 'paragraph']}}). ['StarterKit', 'Underline', 'Link', 'Superscript', 'Subscript', 'Highlight', 'Table', 'TableCell', 'TableHeader', 'TableRow', {'Placeholder': {'placeholder': 'Write or paste content here...'}}, {'TextAlign': {'types': ['heading', 'paragraph']}}, 'Color', 'TextStyle', 'Image'] by default. - focus (number | boolean; optional): If True, the editor will be focused. If False, the editor will be blurred. Can also be a string ('start', 'end', 'all') or number to focus at a specific position. Positive values start at the beginning of the document - negative values at the end. - hiddenFrom (string; optional): Breakpoint above which the component is hidden with `display: none`. - html (string; optional): HTML string representation of the editor content. Affected by debounce. If both json and html are provided, json takes precedence. - json (dict; optional): JSON object (ProseMirror) representation of the editor content. Affected by debounce. If both json and html are provide, json takes precedence. - labels (dict; optional): Labels that are used in controls. If not set, default labels are used. `labels` is a dict with keys: - lightHidden (boolean; optional): Determines whether component should be hidden in light color scheme with `display: none`. - loading_state (dict; optional): Object that holds the loading state object coming from dash-renderer. For use with dash<3. `loading_state` is a dict with keys: - mod (string | dict | list of string | dicts; optional): Element modifiers transformed into `data-` attributes. For example: "xl" or {"data-size": "xl"}. Can also be a list of strings or dicts for multiple modifiers. Falsy values are removed. - n_blur (number; optional): An integer that represents the number of times that this element has lost focus. - persisted_props (list of strings; optional): Properties whose user interactions will persist after refreshing the component or the page. Since only `value` is allowed this prop can normally be ignored. - persistence (string | number | boolean; optional): Used to allow user interactions in this component to be persisted when the component - or the page - is refreshed. If `persisted` is truthy and hasn't changed from its previous value, a `value` that the user has changed while using the app will keep that change, as long as the new `value` also matches what was given originally. Used in conjunction with `persistence_type`. Note: The component must have an `id` for persistence to work. - persistence_type (a value equal to: 'local', 'session', 'memory'; optional): Where persisted user changes will be stored: memory: only kept in memory, reset on page refresh. local: window.localStorage, data is kept after the browser quit. session: window.sessionStorage, data is cleared once the browser quit. - selected (string; optional): Currently selected text. Affected by debounce. - styles (boolean | number | string | dict | list; optional): Adds inline styles directly to inner elements of a component. See Styles API docs. - tabIndex (number; optional): tab-index. - toolbar (dict; optional): Toolbar property definition. Empty by default. `toolbar` is a dict with keys: - variant (a value equal to: 'default', 'subtle'; optional): Variant of the editor. - visibleFrom (string; optional): Breakpoint below which the component is hidden with `display: none`. - withCodeHighlightStyles (boolean; optional): Determines whether code highlight styles should be added, True by default. - withTypographyStyles (boolean; optional): Determines whether typography styles should be added, True by default.