Package Exports
- react-textflux
- react-textflux/dist/react-textflux.css
Readme
React Textflux
π What's New
- Production ready: All major bugs fixed, UX polished
- Enhanced Emoji Picker: Type
:in editor to trigger emoji search, keyboard navigation with arrow keys, auto-scroll, recently used emojis, and category organization - Mention dropdown: Keyboard navigation auto-scrolls, dark/light theme highlight, visible text always
- Emoji/Mention popup closes on outside click: Emoji picker and mention list now close automatically when you click outside the editor or popup.
- Emoji picker close button: Emoji picker now has a close (Γ) button in the top-right corner.
- Popup positioning: Emoji/mention list will open outside the editor (above or below) if there isnβt enough space inside, preventing overlap with text.
- No fallback emoji rendering: Only native emoji rendering is used; fallback logic for unsupported emojis has been removed.
- Media skeleton loader: Shows animated skeleton while uploading/inserting images/videos
- Unified media handling: Paste, drag-and-drop, and toolbar all use the same upload logic
- Accessibility: All dropdowns and toolbars are keyboard accessible
- CSS isolation: All classes prefixed with
tf-(no conflicts) - Media fullscreen reliability improved: Fullscreen preview now works for all images/videos, even if they are nested or dynamically loaded. Event listeners are always correctly attached for robust fullscreen handling.
- Smart cursor positioning: Cursor automatically moves to the end after inserting media, mentions, or emojis
- Improved button reliability: Fixed image/video insert buttons that sometimes didn't work
- Enhanced error handling: Better error handling for file operations and media uploads
- Focus management: Improved focus handling between editor and toolbar
- Existing media support: All existing images/videos in editor content are automatically clickable for fullscreen preview
- Code block UX: When you insert a code block, a new line is automatically added after it, but focus stays inside the code block so you can start typing code immediately. Move out of the code block with arrow keys or mouse to continue writing.
- Precise media click target: Only the actual image/video is clickable for fullscreen; surrounding margins are not clickable.
- Robust fullscreen binding: Existing media always get a click listener even if classes are pre-applied in persisted HTML, fixing rare cases where the first media wouldn't open fullscreen in production.
A minimal, modular, and customizable React rich text editor component with:
Basic formatting (bold, italic, underline, strikethrough, blockquote, lists)
@mention with profile pic/initials
Enhanced Emoji Picker: Type
:to search emojis, keyboard navigation, recently used, categoriesMedia rendering (image/video) with fullscreen preview
Code block support with text wrapping and monospace font
Dark/light theme support
Custom upload logic via callback
No CSS framework dependency (pure CSS, all classes prefixed with
tf-for isolation)Optional fullscreen media preview
Smart cursor positioning for seamless writing experience
Existing media support for fullscreen preview
Enhanced Emoji Picker
Trigger Emoji Search
- Type
:in editor to open emoji picker with search functionality - Real-time search as you type after
:(e.g.,:smile,:heart,:food) - Smart line break handling: Emoji picker works correctly on new lines (after pressing Enter)
- Prevents emoticon interference: Typing
:followed by-(like:-)) won't trigger emoji picker - Keyboard navigation with arrow keys (ββββ) and Enter to select
- Auto-scroll keeps selected emoji visible during navigation
- Click outside or close button to close emoji picker
Emoji Categories & Recently Used
- Recently used emojis appear at the top for quick access
- Category organization: Smileys, Hearts, Food, Animals, Nature, Activity, Travel, Objects
- Popular emojis pre-loaded in recently used section
- Local storage remembers your frequently used emojis
Keyboard Navigation
- Arrow keys for grid navigation (up/down for rows, left/right for columns)
- Enter to select highlighted emoji
- Click outside or close button to close picker
- Auto-scroll follows your navigation smoothly
- Mouse hover also updates selection
Usage Examples
Type in editor: ":smile" β Shows all smile-related emojis
Type in editor: ":heart" β Shows all heart/love emojis
Type in editor: ":food" β Shows all food emojis
Type in editor: ":-)" β Does NOT trigger emoji picker (emoticon)
Type in editor: ":-(" β Does NOT trigger emoji picker (emoticon)Code Block Support
- Insert code blocks using the toolbar button (</>) or keyboard shortcut
Ctrl+K(Cmd+K on Mac) - Paste code from any IDE and it will auto-format as a code block
- Text wrapping and horizontal scroll for long lines
- Monospace font for code clarity
- Continue typing after code: After pasting/inserting code, a new line is added so you can keep writing. Focus remains inside the code block so you can start typing code immediately.
- Move out of code block: Use arrow keys or mouse to move to the new line and continue writing after the code block.
- Highlighting: Toolbar button is highlighted when your cursor is inside a code block
Tip: After inserting a code block, just start typing your code. To continue writing outside the code block, use the arrow keys or click on the new line below the code block.
Install
npm install react-textfluxUsage
// Only default import is supported:
import Editor from 'react-textflux';
import "react-textflux/dist/react-textflux.css";
function App() {
// Example: controlled usage with backend value
const [description, setDescription] = React.useState('');
// Simulate fetching from backend
React.useEffect(() => {
// fetch description from backend
setTimeout(() => setDescription('<p>Initial <b>description</b> from backend</p>'), 1000);
}, []);
// Example: custom upload logic (S3, base64, etc.)
const handleMediaUpload = async (file, type) => {
// Upload file to your server or S3, return { url, type, name }
// Or fallback to base64:
const toBase64 = file => new Promise((res, rej) => {
const reader = new FileReader();
reader.onload = () => res(reader.result);
reader.onerror = rej;
reader.readAsDataURL(file);
});
const url = await toBase64(file);
return { url, type, name: file.name };
};
// Example: mention data
const mentions = [
{ id: 1, name: 'John Doe', profile_pic: 'https://example.com/john.jpg' },
{ id: 2, name: 'Jane Smith', profile_pic: 'https://example.com/jane.jpg' },
{ id: 3, name: 'Bob Johnson' } // without profile_pic
];
// Example: custom API call and clear on Enter
const handleEnter = async (e) => {
// 1. API call
await fetch('/your-api', { method: 'POST', body: JSON.stringify({ content: description }) });
// 2. Clear editor (controlled mode)
setDescription('');
// (Uncontrolled: e.target.innerHTML = '';)
};
return (
<Editor
theme="light" // or "dark"
mentions={mentions}
onMediaUpload={handleMediaUpload}
value={description} // controlled value
onChange={setDescription} // updates state on any content change
mediaFullscreen={true} // <-- Add this line to enable fullscreen media preview
onEnter={handleEnter} // <-- Called when Enter is pressed
/>
);
}Note: The
valueprop makes the editor controlled, just like a textarea. Pass your HTML string tovalue, and update it viaonChange. All content changes (typing, media insert, mentions, formatting) will triggeronChangewith the latest HTML.
Note: The
onEnterprop is called whenever the user presses Enter in the editor. You can use it for custom actions like submitting, saving, or clearing the editor. In controlled mode, clear the editor by setting your value state to ''. In uncontrolled mode, usee.target.innerHTML = ''inside onEnter.
Media Fullscreen Preview
- Media Fullscreen Preview: Click any image or video in the editor to open a fullscreen overlay preview. Click the close (Γ) button or outside the media to exit preview.
- Works with all media: Both newly inserted and existing media (from database, etc.) are clickable for fullscreen preview when
mediaFullscreen={true}is enabled. - Click area: Only the media itself (the
img/video) is clickable; centered side margins are not, preventing accidental opens when you click outside the media.
Tip: You can customize the fullscreen overlay style by editing the relevant CSS in the source.
Smart Cursor Positioning
The editor now automatically positions the cursor in the most logical place after various operations:
- Media Insertion: After inserting images/videos via toolbar, drag & drop, or paste, the cursor moves to the end so you can continue typing
- Mention Insertion: After selecting a mention from the dropdown, cursor moves to the end
- Emoji Insertion: After inserting an emoji, cursor moves to the end with automatic space
- Seamless Writing: No more manual cursor positioning - just keep typing naturally
- Code Block Paste: After pasting code, a new line is added so you can keep typing outside the code block
Features
- Formatting: Bold, Italic, Underline, Strikethrough, Blockquote, Lists
- @Mention: User list with profile pic/initials, keyboard navigation, auto-scroll
- Enhanced Emoji Picker: Type
:to search, keyboard navigation, recently used, categories, auto-scroll - Smart line break handling: Emoji picker works correctly on new lines (after pressing Enter)
- Prevents emoticon interference: Typing
:followed by-(like:-)) won't trigger emoji picker - Popup closes on outside click: Emoji picker and mention list close when clicking outside
- Emoji picker close button: Manual close (Γ) button in emoji picker
- Popup positioning: Picker/list opens outside editor if needed, never overlaps text
- Native emoji rendering only: No fallback for unsupported emojis
- Media: Render images/videos (upload logic is up to you), click to fullscreen preview
- Media Skeleton: Animated skeleton loader while uploading/inserting
- Code Block: Insert code blocks via toolbar or Ctrl+K, auto-format on paste, text wrapping, monospace font, auto new line after code block with focus inside code block
- Theme: Light & dark mode (prop)
- Keyboard Shortcuts: Tooltips show shortcuts (e.g. Ctrl+B, Ctrl+K)
- Custom CSS: No Tailwind/Bootstrap required
- CSS Isolation: All classes prefixed with
tf-(no conflicts with other frameworks) - Dark Theme Polish: Selected toolbar/mention is deep blue for better contrast
- Emoji/Mention Scrollbar: Thin, theme-aware, and modern
- Accessibility: All dropdowns and toolbars are keyboard accessible
- Optional Media Fullscreen: Enable fullscreen preview for images/videos by setting
mediaFullscreen={true}on the Editor. If not set, media is not clickable by default. - Smart Cursor Positioning: Automatic cursor positioning after media, mention, emoji, and code block insertion
- Reliable Button Operation: Fixed image/video insert buttons for consistent functionality
- Enhanced Error Handling: Better error handling and debugging for file operations
- Existing Media Support: All existing images/videos in editor content automatically get fullscreen functionality
Keyboard Shortcuts
| Action | Shortcut |
|---|---|
| Bold | Ctrl+B / Cmd+B |
| Italic | Ctrl+I / Cmd+I |
| Underline | Ctrl+U / Cmd+U |
| Strikethrough | Ctrl+Shift+S / Cmd+Shift+S |
| Blockquote | Ctrl+Q / Cmd+Q |
| Ordered List | Ctrl+Shift+L / Cmd+Shift+L |
| Unordered List | Ctrl+Shift+U / Cmd+Shift+U |
| Code Block | Ctrl+K / Cmd+K |
Emoji Navigation
| Action | Key |
|---|---|
| Open emoji search | Type : in editor |
| Navigate emojis | Arrow keys (ββββ) |
| Select emoji | Enter |
| Close picker | Escape |
Props
| Prop | Type | Default | Description |
|---|---|---|---|
theme |
string | 'light' | 'light' or 'dark' |
mentions |
array | [] | Array of user objects: [{id, name, profile_pic?}] |
onMediaUpload |
function | undefined | Custom upload handler: (file, type) => Promise<{url, type, name}> |
value |
string | undefined | Controlled value: HTML string to display in the editor. Use with onChange for controlled usage. |
onChange |
function | undefined | Called with latest HTML string on any content change (typing, media, mentions, formatting, etc). |
mediaFullscreen |
boolean | false | If true, images/videos are clickable and open in fullscreen overlay. If false or not set, media is not clickable. |
onEnter |
function | undefined | Called with the keyboard event when Enter is pressed in the editor. Use for custom submit, save, or clear logic. |
onEnter: API Call & Clear Example
Controlled mode:
<Editor
value={description}
onChange={setDescription}
onEnter={async () => {
await fetch('/api/save', { method: 'POST', body: JSON.stringify({ content: description }) });
setDescription(''); // clear editor
}}
/>Uncontrolled mode:
<Editor
onEnter={async (e) => {
await fetch('/api/save', { method: 'POST', body: JSON.stringify({ content: e.target.innerHTML }) });
e.target.innerHTML = '';
}}
/>Customization
- Mentions: Pass your user list as
mentionsprop - Emoji List: Edit
emojiData.jsto add/modify emojis and categories - CSS: Edit
src/index.cssfor full style control (all classes usetf-prefix) - Toolbar/Theme: Update colors in
src/index.cssfor your brand - Media Fullscreen: Use the
mediaFullscreenprop to control whether media is clickable for fullscreen preview.
Troubleshooting
Image/Video Insert Buttons Not Working
- Fixed in latest version: The buttons now use proper event handling and should work reliably
- If issues persist, check browser console for error messages
- Ensure file input permissions are granted
Emoji Picker Not Opening
- Type
:in editor to trigger emoji search - Ensure no other keyboard shortcuts are conflicting
- Check browser console for any JavaScript errors
[plugin:vite:import-analysis] Failed to resolve entry for package
- Make sure your
package.jsonhas correctmain,module, andexportsfields - Clear node_modules and reinstall:
rm -rf node_modules package-lock.json && npm install
Image/Video Not Opening in Fullscreen
- Fixed in latest version: Fullscreen preview now works for all images/videos, even if they are nested inside other elements or loaded dynamically. The editor robustly attaches event listeners to all media elements after any content change.
- If you still face issues, ensure you are using the latest version and check for JavaScript errors in the console.