A modern, feature-rich React-based rich text editor powered by Lexical with customizable toolbars and floating toolbar support.
π Live Demo: https://luxe-tools.github.io/luxe-edit/
luxe-edit/
βββ packages/
β βββ core/ # The main package to be published
β βββ src/
β β βββ index.tsx # Main entry point, exports LuxeEditor
β β βββ styles.css # CSS styles for the editor
β β βββ types/
β β β βββ toolbar.ts # Toolbar item types
β β βββ plugins/
β β βββ Toolbar.tsx # Top toolbar component
β β βββ FloatingToolbarPlugin.tsx # Floating toolbar plugin
β βββ dist/ # Built output (generated)
β βββ package.json # Package configuration
β βββ tsconfig.json # TypeScript config
β βββ tsup.config.ts # Build configuration
β
βββ apps/
β βββ demo/ # Demo/example app
β βββ src/
β β βββ main.tsx # Demo app entry point
β βββ index.html # HTML template
β βββ vite.config.ts # Vite configuration
β βββ package.json # Demo app dependencies
β
βββ package.json # Root monorepo config
βββ yarn.lock # Dependency lock file
- β Text Formatting: Bold, Italic, Underline, Strikethrough
- β Headings: H1, H2, H3, H4, H5, H6
- β Alignment: Left, Center, Right, Justify
- β History: Undo/Redo with proper state tracking
- β Top Toolbar: Fixed toolbar at the top of the editor
- β Floating Toolbar: Appears above selected text (3-4 essential options)
- β Customizable: Array-based configuration for toolbar items
- β Active States: Visual feedback for active formatting
- β TypeScript: Full TypeScript support with exported types
- β Customizable: Custom toolbar items, labels, and icons
- β Theme Support: CSS variable-based theming
- β Monorepo: Clean workspace structure
π View Live Demo β
# Using npm
npm install luxe-edit
# Using yarn
yarn add luxe-edit
# Using pnpm
pnpm add luxe-editimport { LuxeEditor } from 'luxe-edit';
import 'luxe-edit/index.css';
function App() {
return (
<LuxeEditor
initialConfig={{
namespace: 'MyEditor',
theme: {}
}}
/>
);
}import { LuxeEditor, type ToolbarItem } from 'luxe-edit';
import 'luxe-edit/index.css';
function App() {
const toolbarItems: ToolbarItem[] = [
{ type: 'undo' },
{ type: 'redo' },
{ type: 'divider' },
{ type: 'bold' },
{ type: 'italic' },
{ type: 'underline' },
{ type: 'strikethrough' },
{ type: 'divider' },
{ type: 'heading1' },
{ type: 'heading2' },
{ type: 'heading3' },
{ type: 'divider' },
{ type: 'alignLeft' },
{ type: 'alignCenter' },
{ type: 'alignRight' },
{ type: 'alignJustify' },
];
return (
<LuxeEditor
initialConfig={{ namespace: 'MyEditor', theme: {} }}
showToolbar={true}
showFloatingToolbar={true}
toolbarItems={toolbarItems}
/>
);
}interface LuxeEditorProps {
initialConfig: any; // Lexical editor initial config
showFloatingToolbar?: boolean; // Show floating toolbar (default: true)
showToolbar?: boolean; // Show top toolbar (default: false)
toolbarItems?: ToolbarItem[]; // Toolbar items for top toolbar
floatingToolbarItems?: ToolbarItem[]; // Separate items for floating toolbar (optional)
children?: React.ReactNode; // Custom plugins/components
}interface ToolbarItem {
type: ToolbarItemType;
label?: string; // Optional custom label
icon?: React.ReactNode; // Optional custom icon
}
type ToolbarItemType =
| 'undo'
| 'redo'
| 'divider'
| 'bold'
| 'italic'
| 'underline'
| 'strikethrough'
| 'heading1' | 'heading2' | 'heading3' | 'heading4' | 'heading5' | 'heading6'
| 'paragraph'
| 'alignLeft'
| 'alignCenter'
| 'alignRight'
| 'alignJustify';bold- Bold textitalic- Italic textunderline- Underlined textstrikethrough- Strikethrough text
heading1throughheading6- Heading levels
undo- Undo last actionredo- Redo last actionparagraph- Convert to paragraph
alignLeft- Left align textalignCenter- Center align textalignRight- Right align textalignJustify- Justify text
divider- Visual separator between toolbar sections
const toolbarItems: ToolbarItem[] = [
{ type: 'bold', label: 'Bold Text' },
{ type: 'italic', icon: <em>I</em> },
{ type: 'divider' },
{ type: 'heading1' },
];<LuxeEditor
initialConfig={{
namespace: 'MyEditor',
theme: {
text: {
bold: 'my-bold-class',
italic: 'my-italic-class',
}
}
}}
/>The floating toolbar automatically filters to show only essential text formatting options (bold, italic, underline). You can customize it:
<LuxeEditor
toolbarItems={fullToolbarItems} // All options for top toolbar
floatingToolbarItems={[ // Limited options for floating toolbar
{ type: 'bold' },
{ type: 'italic' },
{ type: 'underline' }
]}
showToolbar={true}
showFloatingToolbar={true}
/># Install all dependencies
yarn install
# Build the core package
yarn build
# Run the demo app
yarn dev
# Or use workspace commands
yarn workspace luxe-edit build
yarn workspace demo dev- Edit source files in
packages/core/src/ - Rebuild:
yarn build(inpackages/core) - Demo app will hot-reload automatically
- Root
package.json: Defines workspaces (packages/*,apps/*) - Local linking: The
demoapp automatically uses the localluxe-editpackage - Shared dependencies: Common dependencies are hoisted to the root
node_modules
Build Process:
- Source:
src/index.tsx(TypeScript + React/JSX) - Builder:
tsup(fast build tool powered by esbuild) - Output:
dist/folder containing:index.mjs- ESM format (modern)index.js- CommonJS format (Node.js)index.d.ts- TypeScript declarationsindex.css- CSS styles
Package Configuration:
{
"name": "luxe-edit",
"main": "./dist/index.js", // CommonJS entry
"module": "./dist/index.mjs", // ESM entry
"types": "./dist/index.d.ts", // TypeScript types
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},
"./index.css": "./dist/index.css"
},
"files": ["dist"] // Only publish dist folder
}-
Ensure package is built:
cd packages/core yarn build -
Update version in
packages/core/package.json:{ "version": "1.0.1" // Bump version } -
Verify
dist/contains:index.js,index.mjs,index.d.ts,index.css
cd packages/core
# Login to npm (first time only)
npm login
# Publish (with 2FA code)
npm publish --otp=YOUR_6_DIGIT_CODE
# Or use version bumping
npm version patch # 1.0.0 -> 1.0.1
npm publish --otp=YOUR_6_DIGIT_CODEOnly files in the "files" array (dist/) are published:
- β
dist/index.js - β
dist/index.mjs - β
dist/index.d.ts - β
dist/index.css - β
src/,tsconfig.json,tsup.config.ts(not included)
- Always visible at the top of the editor
- Shows active formatting states
- Supports all toolbar item types
- Fully customizable via
toolbarItemsprop
- Appears above selected text
- Automatically filters to show only essential options (3-4 items)
- Only shows: bold, italic, underline, strikethrough
- Can be customized via
floatingToolbarItemsprop
The toolbar tracks and displays active formatting:
- Bold/Italic/Underline/Strikethrough: Highlights when active
- Headings: Shows which heading level is active
- Undo/Redo: Disabled when not available
Full support for headings H1-H6:
- Proper Lexical HeadingNode integration
- Active state tracking
- Seamless conversion between headings and paragraphs
- Styled headings via CSS
<LuxeEditor initialConfig={{ namespace: 'MyEditor' }} />
// Shows floating toolbar with bold, italic, underlineconst toolbarItems: ToolbarItem[] = [
{ type: 'undo' },
{ type: 'redo' },
{ type: 'divider' },
{ type: 'bold' },
{ type: 'italic' },
{ type: 'underline' },
{ type: 'strikethrough' },
{ type: 'divider' },
{ type: 'heading1' },
{ type: 'heading2' },
{ type: 'heading3' },
{ type: 'divider' },
{ type: 'alignLeft' },
{ type: 'alignCenter' },
{ type: 'alignRight' },
{ type: 'alignJustify' },
];
<LuxeEditor
initialConfig={{ namespace: 'MyEditor' }}
showToolbar={true}
toolbarItems={toolbarItems}
/><LuxeEditor
initialConfig={{ namespace: 'MyEditor' }}
showToolbar={false}
showFloatingToolbar={true}
floatingToolbarItems={[
{ type: 'bold' },
{ type: 'italic' },
{ type: 'underline' }
]}
/><LuxeEditor
initialConfig={{ namespace: 'MyEditor' }}
showToolbar={true}
>
{/* Add custom Lexical plugins */}
<MyCustomPlugin />
</LuxeEditor>cd packages/core
# Create a tarball (without publishing)
npm pack
# This creates: luxe-edit-1.0.0.tgz
# In another project, test it:
npm install /path/to/luxe-edit-1.0.0.tgz- Update version in
package.json - Add description, keywords, repository URL to
package.json - Write comprehensive README (this file)
- Fix all TypeScript
anytypes - Add
prepublishOnlyscript - Test installation:
npm pack - Test the tarball locally
- Update changelog
- Tag git release
- Run
npm publish --otp=YOUR_6_DIGIT_CODE
- β Monorepo structure - Clean separation of package and demo
- β Modern build setup - Uses tsup (fast, based on esbuild)
- β Dual format output - ESM + CommonJS for maximum compatibility
- β TypeScript support - Full TypeScript with exported types
- β CSS included - Styles are part of the package
- β React externalized - React is not bundled (prevents duplicates)
- β
Proper exports - Modern
exportsfield with types first - β Customizable toolbars - Array-based configuration
- β Floating toolbar - Context-aware toolbar for selected text
- β Active state tracking - Visual feedback for formatting
- β Heading support - Full H1-H6 support
- β Text formatting - Bold, Italic, Underline, Strikethrough
- β Alignment - Left, Center, Right, Justify
- β History - Undo/Redo with proper state management
{
"description": "A beautiful, customizable rich text editor for React built on Lexical",
"keywords": ["react", "editor", "lexical", "rich-text", "wysiwyg", "toolbar"],
"repository": {
"type": "git",
"url": "https://github.com/yourusername/luxe-edit.git"
},
"homepage": "https://github.com/yourusername/luxe-edit#readme"
}{
"scripts": {
"build": "tsup",
"prepublishOnly": "yarn build"
}
}Replace any types with proper Lexical types:
import { InitialConfigType } from '@lexical/react/LexicalComposer';
export interface LuxeEditorProps {
initialConfig: Partial<InitialConfigType>;
// ...
}{
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
}
}LuxeEdit is a production-ready rich text editor package with:
- β Complete toolbar system (top + floating)
- β Full formatting support (text, headings, alignment)
- β Active state tracking
- β Customizable toolbar items
- β TypeScript support
- β Modern build setup
- β Ready for npm publishing
The architecture follows modern best practices for React component libraries! π