A powerful, customizable rich text editor for React applications with dynamic template suggestions, variable highlighting, and Markdown support.
- 🚀 Rich Text Editing - Based on Quill.js with support for formatting
- 🔍 Dynamic Suggestions - Customizable template variables with autocompletion
- 💡 Variable Highlighting - Automatic highlighting of template variables
- ⌨️ Keyboard Navigation - Full keyboard support for power users
- 🎨 Theming Support - Choose between 'Snow' and 'Bubble' themes
- 🎭 Custom Rendering - Completely customize suggestion items appearance
- 🧩 Markdown Support - Automatic Markdown formatting
- 📱 Responsive Design - Works on all screen sizes
npm install dynamic-text-editor
# or
yarn add dynamic-text-editorThis package has the following peer dependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
quill: ^1.3.7
import { DynamicTextEditor } from 'dynamic-text-editor';
import 'dynamic-text-editor/dist/styles.css'; // Import default styles
const MyEditor = () => {
const [content, setContent] = useState('Hello {{user.name}}!');
const suggestions = [
{
id: 'user.name',
label: 'User Name',
value: 'user.name',
description: 'The name of the current user',
category: 'User'
},
// More suggestions...
];
return (
<DynamicTextEditor
value={content}
onChange={setContent}
suggestions={suggestions}
placeholder="Start typing..."
/>
);
};| Prop | Type | Default | Description |
|---|---|---|---|
value |
string |
'' |
The content of the editor |
onChange |
function |
required | Callback when content changes |
suggestions |
array |
[] |
Array of suggestion items |
placeholder |
string |
'' |
Placeholder text when editor is empty |
theme |
'snow' | 'bubble' |
'snow' |
Editor theme |
toolbar |
array |
[] |
Toolbar configuration |
fontSize |
string |
'1rem' |
Font size for editor content |
lineHeight |
string |
'1.5' |
Line height for editor content |
width |
string |
'100%' |
Width of the editor |
height |
string |
'auto' |
Height of the editor |
renderItem |
function |
undefined |
Custom renderer for suggestion items |
classNames |
object |
{} |
Custom class names for components |
trigger |
string |
'{{' |
The trigger string for suggestions |
closingChar |
string |
'}}' |
The closing string for templates |
maxHeight |
number |
300 |
Maximum height of suggestions dropdown |
minWidth |
number |
200 |
Minimum width of suggestions dropdown |
maxWidth |
number |
400 |
Maximum width of suggestions dropdown |
Each suggestion item should have the following structure:
interface BaseEditorItem {
id: string; // Unique identifier
label: string; // Display label
value: string; // Value to insert
description?: string; // Optional description
category?: string; // Optional category
link?: string; // Optional documentation link
}The editor supports two built-in themes:
// Snow theme (default)
<DynamicTextEditor theme="snow" />
// Bubble theme
<DynamicTextEditor theme="bubble" />You can customize the appearance by providing custom class names:
<DynamicTextEditor
classNames={{
root: "my-editor",
editor: "my-editor__input",
variable: "my-editor__variable",
suggestions: "my-editor__suggestions",
suggestion: "my-editor__suggestion",
suggestionSelected: "my-editor__suggestion--selected",
category: "my-editor__category",
description: "my-editor__description",
}}
/>You can target the editor with CSS selectors:
/* Target the editor container */
.dynamic-text-editor {
border: 2px solid #3498db;
}
/* Style the variable highlights */
.dynamic-text-editor__variable {
background-color: #e8f4fd;
color: #2980b9;
padding: 2px 4px;
border-radius: 4px;
}
/* Style the suggestions dropdown */
.dynamic-text-editor__suggestions {
border: 1px solid #e2e8f0;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}Completely customize the appearance of suggestion items:
const renderCustomItem = (item, isSelected) => {
return (
<div
style={{
padding: "12px 16px",
backgroundColor: isSelected ? "#f0f9ff" : "transparent",
cursor: "pointer",
borderLeft: isSelected ? "3px solid #0066cc" : "3px solid transparent",
display: "flex",
gap: "12px",
alignItems: "flex-start",
}}
>
<div style={{ fontSize: "20px" }}>
{getIconForCategory(item.category)}
</div>
<div>
<div style={{ fontWeight: "600" }}>{item.label}</div>
{item.description && (
<div style={{ fontSize: "0.9em", color: "#666" }}>
{item.description}
</div>
)}
{item.category && (
<div style={{ fontSize: "0.8em", color: "#888" }}>
{item.category}
</div>
)}
</div>
</div>
);
};
<DynamicTextEditor
renderItem={renderCustomItem}
// ...
/>Configure the toolbar with an array of formatting options:
<DynamicTextEditor
toolbar={[
['bold', 'italic', 'underline', 'strike'],
['blockquote', 'code-block'],
[{ 'header': 1 }, { 'header': 2 }],
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
[{ 'indent': '-1'}, { 'indent': '+1' }],
[{ 'direction': 'rtl' }],
[{ 'size': ['small', false, 'large', 'huge'] }],
[{ 'color': [] }, { 'background': [] }],
[{ 'align': [] }],
['clean']
]}
/>You can access the editor's API using a ref:
import { useRef } from 'react';
import { DynamicTextEditor, DynamicTextEditorRef } from 'dynamic-text-editor';
const MyEditor = () => {
const editorRef = useRef<DynamicTextEditorRef>(null);
const handleClearClick = () => {
// Access editor methods through ref
editorRef.current?.clearContent();
};
return (
<>
<DynamicTextEditor
ref={editorRef}
// ...other props
/>
<button onClick={handleClearClick}>Clear Editor</button>
</>
);
};Available methods on the ref:
clearContent()- Clear the editor contentfocus()- Focus the editorblur()- Remove focus from the editor
Allow users to switch between themes:
const [theme, setTheme] = useState<'snow' | 'bubble'>('snow');
<div>
<select
value={theme}
onChange={(e) => setTheme(e.target.value as 'snow' | 'bubble')}
>
<option value="snow">Snow Theme</option>
<option value="bubble">Bubble Theme</option>
</select>
<DynamicTextEditor
theme={theme}
// ...other props
/>
</div>const [showToolbar, setShowToolbar] = useState(true);
const toolbarOptions = [['bold', 'italic', 'underline']];
<div>
<label>
<input
type="checkbox"
checked={showToolbar}
onChange={() => setShowToolbar(!showToolbar)}
/>
Show Toolbar
</label>
<DynamicTextEditor
toolbar={showToolbar ? toolbarOptions : []}
// ...other props
/>
</div><DynamicTextEditor
trigger="${"
closingChar="}"
// ...other props
/>You can easily add a real-time preview that updates as the user types in the editor:
import { useState, useEffect } from 'react';
import { DynamicTextEditor } from 'dynamic-text-editor';
const EditorWithPreview = () => {
const [content, setContent] = useState('Hello {{user.name}}!');
const [previewContent, setPreviewContent] = useState([]);
// Function to highlight variables in preview
const highlightVariables = (text) => {
if (!text) return [];
// Regular expression to find {{variables}}
const regex = /{{([^{}]*)}}/g;
let lastIndex = 0;
const parts = [];
let match;
// Find all matches and create array of text and highlighted variables
while ((match = regex.exec(text)) !== null) {
// Add text before the variable
if (match.index > lastIndex) {
parts.push(text.substring(lastIndex, match.index));
}
// Add the highlighted variable
parts.push(
<span className="preview-variable" key={match.index}>
{match[0]}
</span>
);
lastIndex = match.index + match[0].length;
}
// Add any remaining text after the last variable
if (lastIndex < text.length) {
parts.push(text.substring(lastIndex));
}
return parts;
};
// Update preview when content changes
useEffect(() => {
setPreviewContent(highlightVariables(content));
}, [content]);
return (
<div>
<DynamicTextEditor
value={content}
onChange={setContent}
suggestions={[...]}
// ...other props
/>
<div className="preview-section">
<h3>Live Preview</h3>
<div className="preview-content">
{previewContent}
</div>
</div>
</div>
);
};CSS to style the preview:
.preview-section {
margin-top: 2rem;
padding: 1.5rem;
background-color: #f9f9fb;
border-radius: 8px;
border: 1px solid #e8e8ed;
}
.preview-content {
padding: 1rem;
background-color: white;
border-radius: 6px;
border: 1px solid #eaeaef;
min-height: 4rem;
line-height: 1.5;
font-size: 1.1rem;
}
.preview-variable {
display: inline-block;
background-color: rgba(0, 102, 204, 0.08);
color: #0066cc;
padding: 0.1rem 0.3rem;
border-radius: 3px;
border: 1px solid rgba(0, 102, 204, 0.2);
font-weight: 500;
}The preview section will update in real-time as users type in the editor, with variables highlighted for better visibility.
