A modern, flexible rich text editor for React applications, built on top of Quill.js. Provides both hook and component APIs for maximum flexibility.
dynamic-prompt-editor.1.mp4
- 🎨 Two beautiful themes (Snow & Bubble)
- 🌓 First-class dark mode support
- đź› Fully customizable toolbar
- 📱 Responsive & mobile-friendly
- 🎯 Written in TypeScript
- ⚡️ Lightweight & performant
- 🔄 Real-time content updates
- 🎮 Controlled & uncontrolled modes
- 📦 Zero configuration needed
- Install the package:
npm install dynamic-text-editor quill
# or
yarn add dynamic-text-editor quill
# or
pnpm add dynamic-text-editor quill- Import the styles:
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";- Use it in your app:
import { DynamicTextEditor } from "dynamic-text-editor";
function App() {
return <DynamicTextEditor theme="snow" placeholder="Start typing..." onChange={(value) => console.log(value)} />;
}Follow these steps to set up the project locally for development or testing:
git clone https://github.com/konpeeyushDelta4/dynamic-text-editor.git
cd dynamic-text-editor# Install dependencies in the root directory
npm install
# or
yarn install
# or
pnpm install
# Build the library
npm run build
# or
yarn build
# or
pnpm build# Navigate to the test project
cd Dynamic-Text-Editor-Test
# Install dependencies
npm install
# or
yarn install
# or
pnpm install
# Start the development server
npm run dev
# or
yarn dev
# or
pnpm devYour test application should now be running at http://localhost:5173
# In the main library directory
npm test
# or
yarn test
# or
pnpm test
# In the test project directory
cd Dynamic-Text-Editor-Test
npm test
# or
yarn test
# or
pnpm test# Build the main library for production
npm run build
# or
yarn build
# or
pnpm build
# Build the test project for production
cd Dynamic-Text-Editor-Test
npm run build
# or
yarn build
# or
pnpm buildThe production-ready files will be available in the dist directory.
.
├── package.json # Main package configuration
├── src/ # Source code for the library
│ ├── components/ # React components
│ ├── hooks/ # Custom React hooks
| |---index #
│ └── styles/ # Basic CSS for default Editors
├── dist/ # Built files (after running build)
|
├── example/ # Test application
│ ├── package.json # Test app configuration
│ ├── src/ # Test app source code
│ │ ├── components/ # Test app components
│ │ │ └── use-case/ # Component and Hook usage examples
│ │ ├── App.tsx # Main application component
│ │ └── main.tsx # Entry point
│ ├── public/ # Static assets
│ └── dist/ # Built files for test app
└── README.md # This file
The package provides two ways to use the editor: as a component or as a hook.
The component API provides a ready-to-use editor with all the bells and whistles.
import { DynamicTextEditor } from "dynamic-text-editor";
function MyEditor() {
return <DynamicTextEditor theme="snow" value="Initial content" onChange={(value) => console.log(value)} placeholder="Start typing..." readOnly={false} toolbar={[["bold", "italic"]]} />;
}| Prop | Type | Default | Description |
|---|---|---|---|
theme |
"snow" | "bubble" |
"snow" |
Editor theme |
value |
string |
"" |
Editor content in HTML format |
defaultValue |
string |
"" |
Initial content (uncontrolled mode) |
onChange |
(value: string) => void |
- | Called when content changes |
placeholder |
string |
"" |
Placeholder text when editor is empty |
readOnly |
boolean |
false |
Makes the editor read-only |
toolbar |
ToolbarConfig[] |
Default toolbar | Customizes the toolbar options |
className |
string |
"" |
Class name for the editor wrapper |
containerClassName |
string |
"" |
Class name for the container |
onFocus |
() => void |
- | Called when editor gains focus |
onBlur |
() => void |
- | Called when editor loses focus |
The hook API provides more flexibility and control over the editor instance.
import { useDynamicTextEditor } from "dynamic-text-editor";
function MyEditor() {
const { quillRef, getValue, setValue } = useDynamicTextEditor({
theme: "snow",
onChange: (value) => console.log(value),
});
return (
<>
<div ref={quillRef} />
<button onClick={() => console.log(getValue())}>Get Content</button>
</>
);
}| Option | Type | Default | Description |
|---|---|---|---|
theme |
"snow" | "bubble" |
"snow" |
Editor theme |
onChange |
(value: string) => void |
- | Content change handler |
value |
string |
"" |
Controlled value |
defaultValue |
string |
"" |
Initial value (uncontrolled) |
placeholder |
string |
"" |
Placeholder text |
toolbar |
ToolbarConfig[] |
Default toolbar | Toolbar configuration |
onFocus |
() => void |
- | Focus handler |
onBlur |
() => void |
- | Blur handler |
| Property | Type | Description |
|---|---|---|
quillRef |
RefObject |
Ref to attach to your container |
getValue |
() => string |
Get current editor content |
setValue |
(value: string) => void |
Set editor content |
clear |
() => void |
Clear editor content |
focus |
() => void |
Focus the editor |
blur |
() => void |
Blur the editor |
The toolbar can be customized using the toolbar prop. Here are all available options:
const FULL_TOOLBAR = [
[{ header: [1, 2, 3, 4, 5, 6, false] }],
["bold", "italic", "underline", "strike"],
[{ color: [] }, { background: [] }],
[{ script: "sub" }, { script: "super" }],
[{ list: "ordered" }, { list: "bullet" }],
[{ indent: "-1" }, { indent: "+1" }],
[{ direction: "rtl" }],
[{ align: [] }],
["link", "image", "video"],
["clean"],
];
// Usage
<DynamicTextEditor toolbar={FULL_TOOLBAR} />;- Basic Text Formatting
const basicToolbar = [["bold", "italic", "underline"]];- Rich Text Features
const richToolbar = [["bold", "italic"], [{ header: [1, 2, false] }], ["link", "image"]];- Content Organization
const organizationToolbar = [[{ header: [1, 2, 3, false] }], [{ list: "ordered" }, { list: "bullet" }], [{ align: [] }]];The editor supports both Snow and Bubble themes with dark mode:
// Snow theme with dark mode
<DynamicTextEditor
theme="snow"
containerClassName={`
min-h-[200px] rounded-lg
${isDarkMode
? "border-gray-600 [&_.ql-toolbar]:!bg-[#2c2d31] [&_.ql-toolbar]:!border-gray-600 [&_.ql-container]:!border-gray-600 [&_.ql-editor]:!text-gray-200 [&_.ql-editor]:!bg-[#25262b]"
: "border-gray-200 [&_.ql-toolbar]:!bg-gray-50 [&_.ql-container]:!bg-white"
}
`}
/>
// Bubble theme with dark mode
<DynamicTextEditor
theme="bubble"
containerClassName={`
min-h-[200px] rounded-lg
${isDarkMode
? "[&_.ql-editor]:!border-gray-600 [&_.ql-editor]:!bg-[#25262b] [&_.ql-editor]:!text-gray-200"
: "[&_.ql-editor]:!border [&_.ql-editor]:!border-gray-200 [&_.ql-editor]:!rounded-lg [&_.ql-editor]:!bg-white"
}
`}
/>You can customize the editor's appearance using CSS variables:
.dynamic-text-editor {
--dte-primary-color: #007aff;
--dte-border-radius: 8px;
--dte-toolbar-bg: #f8f9fa;
--dte-editor-bg: #ffffff;
--dte-text-color: #2c3e50;
--dte-placeholder-color: #a0aec0;
}function BasicEditor() {
return <DynamicTextEditor theme="snow" placeholder="Start typing..." onChange={(value) => console.log(value)} />;
}function ControlledEditor() {
const [content, setContent] = useState("");
return <DynamicTextEditor value={content} onChange={setContent} theme="snow" />;
}function CustomEditor() {
const { quillRef } = useDynamicTextEditor({
theme: "snow",
toolbar: [["bold", "italic"], [{ header: [1, 2, false] }]],
onChange: (value) => console.log(value),
});
return <div ref={quillRef} className="min-h-[200px]" />;
}function ReadOnlyEditor() {
return <DynamicTextEditor readOnly value="<p>This content cannot be edited</p>" theme="bubble" />;
}The package includes comprehensive TypeScript definitions. Here are some common types:
type Theme = "snow" | "bubble";
interface ToolbarConfig {
header?: (1 | 2 | 3 | 4 | 5 | 6 | false)[];
bold?: boolean;
italic?: boolean;
// ... other toolbar options
}
interface DynamicTextEditorProps {
theme?: Theme;
value?: string;
onChange?: (value: string) => void;
// ... other props
}- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
- IE 11 (with polyfills)