
Security News
The Hidden Blast Radius of the Axios Compromise
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.
react-summernote-light
Advanced tools
[](https://badge.fury.io/js/react-summernote-light) [](https://opensource.org/licenses/MIT)
A powerful, customizable WYSIWYG editor for React applications built from scratch with modern design and extensive features.
npm install react-summernote-light
This package requires the following peer dependencies:
npm install react react-dom @radix-ui/react-dialog @radix-ui/react-dropdown-menu @radix-ui/react-popover @radix-ui/react-select @radix-ui/react-separator @radix-ui/react-slot @radix-ui/react-tabs @radix-ui/react-toggle @radix-ui/react-tooltip @uiw/react-codemirror lucide-react class-variance-authority clsx tailwind-merge
This component requires Tailwind CSS. Make sure you have it configured in your project:
npm install tailwindcss
tailwind.config.js:module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
"./node_modules/react-summernote-light/dist/**/*.{js,jsx}", // Add this line
],
theme: {
extend: {},
},
plugins: [],
};
import React, { useState } from "react";
import { RichTextEditor } from "react-summernote-light";
function App() {
const [content, setContent] = useState("");
return (
<div className="p-4">
<RichTextEditor
initialValue={content}
onChange={setContent}
placeholder="Start typing your content here..."
minHeight="400px"
/>
</div>
);
}
export default App;
The editor provides extensive toolbar customization through the toolbarConfig prop. You can customize which buttons appear, how they're grouped, and hide specific buttons.
| Button Type | Description |
|---|---|
undo | Undo action |
redo | Redo action |
formatBlock | Heading/paragraph format selector |
fontName | Font family selector |
fontSize | Font size selector |
bold | Bold formatting |
italic | Italic formatting |
underline | Underline formatting |
strikethrough | Strikethrough formatting |
foreColor | Text color picker |
backColor | Background color picker |
justifyLeft | Align left |
justifyCenter | Align center |
justifyRight | Align right |
justifyFull | Justify text |
insertUnorderedList | Bullet list |
insertOrderedList | Numbered list |
indent | Increase indent |
outdent | Decrease indent |
createLink | Insert link |
insertImage | Insert image |
insertVideo | Insert video |
insertTable | Insert table |
insertHorizontalRule | Insert horizontal line |
modeToggle | Toggle between visual/code view |
interface ToolbarConfig {
sections?: ToolbarSection[];
hiddenButtons?: ToolbarButtonType[];
showSeparators?: boolean;
}
interface ToolbarSection {
name: string;
buttons: ToolbarButtonType[];
}
<RichTextEditor
toolbarConfig={{
sections: [
{
name: "text-style",
buttons: ["bold", "italic", "underline", "foreColor"],
},
{
name: "lists",
buttons: ["insertUnorderedList", "insertOrderedList"],
},
{
name: "insert",
buttons: ["createLink", "insertImage"],
},
],
showSeparators: true,
hiddenButtons: [],
}}
/>
<RichTextEditor
toolbarConfig={{
sections: [
{
name: "basic",
buttons: ["bold", "italic", "createLink"],
},
],
showSeparators: false,
}}
/>
<RichTextEditor
toolbarConfig={{
hiddenButtons: ["insertVideo", "insertTable", "foreColor", "backColor"],
}}
/>
<RichTextEditor
showToolbar={false}
placeholder="Clean editor without toolbar..."
/>
import React, { useState } from "react";
import { RichTextEditor, RichTextEditorProps } from "react-summernote-light";
function AdvancedEditor() {
const [content, setContent] = useState(
"<h2>Welcome!</h2><p>Rich text content here...</p>"
);
// Custom image upload handler
const handleImageUpload = async (file: File): Promise<string> => {
const formData = new FormData();
formData.append("image", file);
try {
const response = await fetch("/api/upload", {
method: "POST",
body: formData,
});
const data = await response.json();
return data.url; // Return the uploaded image URL
} catch (error) {
console.error("Upload failed:", error);
throw error;
}
};
return (
<div className="max-w-4xl mx-auto p-6">
<h1 className="text-2xl font-bold mb-4">Rich Text Editor</h1>
<RichTextEditor
initialValue={content}
onChange={setContent}
onImageUpload={handleImageUpload}
placeholder="Start writing something amazing..."
minHeight="500px"
enableCodeView
enableFullscreen
theme="light"
className="border rounded-lg shadow-sm"
onFocus={() => console.log("Editor focused")}
onBlur={() => console.log("Editor blurred")}
/>
<div className="mt-4 p-4 bg-gray-100 rounded">
<h3 className="font-semibold mb-2">Content Preview:</h3>
<div dangerouslySetInnerHTML={{ __html: content }} />
</div>
</div>
);
}
export default AdvancedEditor;
| Prop | Type | Default | Description |
|---|---|---|---|
initialValue | string | '' | Initial HTML content of the editor |
onChange | (content: string) => void | - | Callback fired when content changes |
onFocus | () => void | - | Callback fired when editor gains focus |
onBlur | () => void | - | Callback fired when editor loses focus |
onImageUpload | (file: File) => Promise<string> | - | Custom image upload handler. Should return the uploaded image URL |
placeholder | string | - | Placeholder text shown when editor is empty |
className | string | - | Additional CSS classes for the editor container |
height | string | - | Fixed height of the editor |
minHeight | string | '300px' | Minimum height of the editor |
maxHeight | string | - | Maximum height of the editor |
theme | 'light' | 'dark' | 'light' | Editor theme |
enableCodeView | boolean | false | Enable HTML code view toggle |
enableFullscreen | boolean | false | Enable fullscreen mode |
disableImages | boolean | false | Disable image insertion functionality |
disableVideos | boolean | false | Disable video embedding functionality |
disableTables | boolean | false | Disable table creation and editing |
airMode | boolean | false | Enable air mode (minimal styling) |
toolbarConfig | ToolbarConfig | - | Custom toolbar configuration |
showToolbar | boolean | true | Show/hide the toolbar completely |
interface RichTextEditorProps {
initialValue?: string;
onChange?: (content: string) => void;
onFocus?: () => void;
onBlur?: () => void;
onImageUpload?: (file: File) => Promise<string>;
placeholder?: string;
toolbarItems?: ToolbarButton[];
disableImages?: boolean;
disableVideos?: boolean;
disableTables?: boolean;
className?: string;
height?: string;
minHeight?: string;
maxHeight?: string;
theme?: "light" | "dark";
airMode?: boolean;
enableCodeView?: boolean;
enableFullscreen?: boolean;
}
interface ToolbarButton {
icon: string;
tooltip: string;
command: string;
value?: string;
}
You can customize the editor's appearance using CSS classes:
<RichTextEditor
className="custom-editor border-2 border-blue-500 rounded-xl"
// ... other props
/>
The editor supports light and dark themes:
<RichTextEditor
theme="dark"
className="bg-gray-900 text-white"
// ... other props
/>
You can customize the toolbar by providing your own toolbar items:
const customToolbar = [
{ icon: "bold", tooltip: "Bold", command: "bold" },
{ icon: "italic", tooltip: "Italic", command: "italic" },
// ... more items
];
<RichTextEditor
toolbarItems={customToolbar}
// ... other props
/>;
import { useForm, Controller } from "react-hook-form";
function MyForm() {
const { control, handleSubmit } = useForm();
const onSubmit = (data) => {
console.log(data.content);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="content"
control={control}
render={({ field }) => (
<RichTextEditor
initialValue={field.value}
onChange={field.onChange}
placeholder="Enter your content..."
/>
)}
/>
<button type="submit">Submit</button>
</form>
);
}
import { Formik, Form, Field } from "formik";
function MyFormikForm() {
return (
<Formik
initialValues={{ content: "" }}
onSubmit={(values) => console.log(values)}
>
{({ setFieldValue, values }) => (
<Form>
<RichTextEditor
initialValue={values.content}
onChange={(content) => setFieldValue("content", content)}
placeholder="Enter your content..."
/>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
);
}
import { getStorage, ref, uploadBytes, getDownloadURL } from "firebase/storage";
const storage = getStorage();
const handleImageUpload = async (file: File): Promise<string> => {
const storageRef = ref(storage, `images/${file.name}`);
const snapshot = await uploadBytes(storageRef, file);
const downloadURL = await getDownloadURL(snapshot.ref);
return downloadURL;
};
<RichTextEditor
onImageUpload={handleImageUpload}
// ... other props
/>;
import AWS from "aws-sdk";
const s3 = new AWS.S3({
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
region: "your-region",
});
const handleImageUpload = async (file: File): Promise<string> => {
const params = {
Bucket: "your-bucket-name",
Key: `images/${file.name}`,
Body: file,
ContentType: file.type,
};
const result = await s3.upload(params).promise();
return result.Location;
};
<RichTextEditor
onImageUpload={handleImageUpload}
// ... other props
/>;
git clone https://github.com/shakibgithub944/flexi-rich-text.git
cd flexi-rich-text
npm install
npm run dev
# Build the library
npm run build:lib
# Build the demo app
npm run build:app
# Build both
npm run build
# Create a package tarball
npm pack
# In another project, install the local package
npm install path/to/react-summernote-light-1.0.0.tgz
Contributions are welcome! Please feel free to submit a Pull Request.
git checkout -b feature/AmazingFeature)git commit -m 'Add some AmazingFeature')git push origin feature/AmazingFeature)This project is licensed under the MIT License - see the LICENSE file for details.
If you encounter any issues or have suggestions, please open an issue on GitHub.
Made with ❤️ by shakibgithub944
FAQs
[](https://badge.fury.io/js/react-summernote-light) [](https://opensource.org/licenses/MIT)
We found that react-summernote-light demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.