React editor for Ethereum Comments Protocol
npm install @ecp.eth/react-editor@ecp.eth/react-editorA 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.
See an example of the React Editor in action at share.ethcomments.xyz.
``bash`
npm install @ecp.eth/react-editoror
yarn add @ecp.eth/react-editoror
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
`tsx
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 (
suggestions={suggestions}
uploads={uploads}
onBlur={() => console.log("Editor lost focus")}
/>
);
}
`
The editor also supports React Native! Import the native version and wrap your app with PortalProvider:
`tsx
import { Editor, type EditorRef } from "@ecp.eth/react-editor/editor.native";
import { PortalProvider } from "@gorhom/portal";
import { useIndexerSuggestions } from "@ecp.eth/react-editor/hooks";
import { usePinataUploadFiles } from "@ecp.eth/react-editor/hooks";
function App() {
return (
);
}
function CommentEditor() {
const editorRef = useRef
const suggestions = useIndexerSuggestions();
const uploads = usePinataUploadFiles();
return (
placeholder="Write your comment..."
suggestions={suggestions}
uploads={uploads}
onBlur={() => console.log("Editor lost focus")}
/>
);
}
`
Important: The PortalProvider from @gorhom/portal is required for React Native to ensure the suggestion UI displays correctly. Make sure to wrap your app (or at least the component tree containing the editor) with PortalProvider.
The main editor component with full rich text editing capabilities.
`tsx
import { Editor, type EditorRef } from "@ecp.eth/react-editor";
const editorRef = useRef
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:
`tsx
// 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 = await editorRef.current?.getUploadedFiles();
// Get files pending upload
const pendingFiles = await 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.
`tsx
import { useIndexerSuggestions } from "@ecp.eth/react-editor/hooks";
const suggestions = useIndexerSuggestions({
indexerUrl: "https://api.ethcomments.xyz",
});
`
Provides file upload functionality using Pinata IPFS service.
`tsx
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.
`tsx
import { useHandleDefaultEditorValue } from "@ecp.eth/react-editor/hooks";
const content = useHandleDefaultEditorValue(
defaultValue?.content,
defaultValue?.references,
);
`
Extract structured references from editor content.
`tsx
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.
`tsx
import { parse } from "@ecp.eth/react-editor/parser";
const richContent = parse(plainText, references);
`
`tsx
// 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:
`tsx
import { CustomImageComponent } from "./CustomImageComponent";
import { CustomVideoComponent } from "./CustomVideoComponent";
import { CustomFileComponent } from "./CustomFileComponent";
videoComponent={CustomVideoComponent}
fileComponent={CustomFileComponent}
// ... other props
/>;
`
The package exports comprehensive TypeScript types:
`tsx`
import type {
EditorRef,
EditorProps,
EditorSuggestionsService,
UploadFilesService,
MentionItem,
LinkAttributes,
MentionsExtensionTheme,
} from "@ecp.eth/react-editor/types";
`tsx`
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" },
];
},
};
`tsx`
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)),
);
},
};
- @tanstack/react-query >= 5.0.0pinata
- ^2.5.0react
- 18 || 19react-dom
- 18 || 19viem
- ^2.29.2
All web dependencies plus:
- @gorhom/portal >= 1.0.14 (required for suggestion UI)react-native
- >= 0.81.0react-native-safe-area-context
- >= 5.6.0react-native-reanimated
- >= 4.1.1react-native-webview
- >= 13.16.0react-native-worklets` >= 0.7.1
-
MIT