A React-based rich text editor for the Ethereum Comments Protocol.
The ECP React Editor provides a powerful, customizable rich text editor built on top of TipTap that supports mentions, file uploads, and seamless integration with the Ethereum Comments Protocol. For comprehensive documentation and guides, visit our documentation website.
npm install @ecp.eth/react-editor
# or
yarn add @ecp.eth/react-editor
# or
pnpm add @ecp.eth/react-editor
- Rich Text Editing: Built on TipTap with support for paragraphs, links, and formatting
- Mentions: Support for ENS, Farcaster, and ERC-20 token mentions with autocomplete
- File Uploads: Drag-and-drop file uploads with support for images, videos, and documents
- Reference Extraction: Extract structured references from editor content
- Content Parsing: Parse plain text with references back into rich content
- Customizable Components: Fully customizable media components and themes
- TypeScript Support: Full TypeScript support with comprehensive type definitions
import { Editor } from "@ecp.eth/react-editor";
import { useIndexerSuggestions } from "@ecp.eth/react-editor/hooks";
import { usePinataUploadFiles } from "@ecp.eth/react-editor/hooks";
function CommentEditor() {
const suggestions = useIndexerSuggestions();
const uploads = usePinataUploadFiles();
return (
<Editor
placeholder="Write your comment..."
suggestions={suggestions}
uploads={uploads}
onBlur={() => console.log("Editor lost focus")}
/>
);
}
The main editor component with full rich text editing capabilities.
import { Editor, type EditorRef } from "@ecp.eth/react-editor";
const editorRef = useRef<EditorRef>(null);
<Editor
ref={editorRef}
placeholder="Write your comment..."
suggestions={suggestions}
uploads={uploads}
autoFocus={true}
onBlur={() => console.log("Editor lost focus")}
onEscapePress={() => console.log("Escape pressed")}
/>;
The editor ref provides several useful methods:
// Focus the editor
editorRef.current?.focus();
// Clear editor content
editorRef.current?.clear();
// Add files programmatically
editorRef.current?.addFiles([file1, file2]);
// Get uploaded files
const uploadedFiles = editorRef.current?.getUploadedFiles();
// Get files pending upload
const pendingFiles = editorRef.current?.getFilesForUpload();
// Mark file as uploaded
editorRef.current?.setFileAsUploaded(uploadedFile);
// Mark file upload as failed
editorRef.current?.setFileUploadAsFailed(fileId);
Provides suggestions for ENS, Farcaster, and ERC-20 mentions using the ECP Indexer.
import { useIndexerSuggestions } from "@ecp.eth/react-editor/hooks";
const suggestions = useIndexerSuggestions({
indexerUrl: "https://indexer.ethcomments.xyz",
});
Provides file upload functionality using Pinata IPFS service.
import { usePinataUploadFiles } from "@ecp.eth/react-editor/hooks";
const uploads = usePinataUploadFiles({
pinataApiKey: "your-pinata-api-key",
pinataSecretApiKey: "your-pinata-secret-key",
});
Handles setting default editor values with content and references.
import { useHandleDefaultEditorValue } from "@ecp.eth/react-editor/hooks";
const content = useHandleDefaultEditorValue(
defaultValue?.content,
defaultValue?.references,
);
Extract structured references from editor content.
import { extractReferences } from "@ecp.eth/react-editor/extract-references";
const editorContent = editorRef.current?.editor?.getJSON();
const references = extractReferences(editorContent);
Parse plain text with references back into rich content.
import { parse } from "@ecp.eth/react-editor/parser";
const richContent = parse(plainText, references);
// Default limits
const ALLOWED_UPLOAD_MIME_TYPES = [
"image/png",
"image/jpeg",
"image/gif",
"image/webp",
"video/mp4",
"video/webm",
"video/avi",
"video/quicktime",
];
const MAX_UPLOAD_FILE_SIZE = 1024 * 1024 * 10; // 10MB
You can customize how media files are displayed:
import { CustomImageComponent } from "./CustomImageComponent";
import { CustomVideoComponent } from "./CustomVideoComponent";
import { CustomFileComponent } from "./CustomFileComponent";
<Editor
imageComponent={CustomImageComponent}
videoComponent={CustomVideoComponent}
fileComponent={CustomFileComponent}
// ... other props
/>;
The package exports comprehensive TypeScript types:
import type {
EditorRef,
EditorProps,
EditorSuggestionsService,
UploadFilesService,
MentionItem,
LinkAttributes,
MentionsExtensionTheme,
} from "@ecp.eth/react-editor/types";
const customSuggestions: EditorSuggestionsService = {
search: async (query: string) => {
// Implement your own search logic
return [
{ type: "ens", address: "0x...", name: "example.eth" },
{ type: "farcaster", address: "0x...", fname: "example" },
];
},
};
const customUploads: UploadFilesService = {
allowedMimeTypes: ["image/png", "image/jpeg"],
maxFileSize: 5 * 1024 * 1024, // 5MB
uploadFile: async (file, callbacks) => {
// Implement your own upload logic
const response = await uploadToYourService(file);
callbacks?.onSuccess?.(uploadedFile, response);
return response;
},
uploadFiles: async (files, callbacks) => {
// Implement batch upload logic
return Promise.all(
files.map((file) => customUploads.uploadFile(file, callbacks)),
);
},
};
This package requires the following peer dependencies:
-
@tanstack/react-query
>= 5.0.0 -
pinata
^2.4.3 -
react
18 || 19 -
viem
^2.29.2
MIT