Creating Custom Extensions
Extend LexKit with powerful custom functionality
Learn how to build type-safe extensions that seamlessly integrate with LexKit's editor system. From simple commands to complex UI components, unlock the full potential of your editor.
What Are Extensions?
Extensions are modular pieces of functionality that enhance your LexKit editor. They provide commands, state queries, UI components, and custom behavior.
Define actions that users can trigger. Commands are strongly typed and accessible through the editor's command palette and toolbar.
Query the current state of your editor. Use these to enable/disable buttons, show contextual UI, or make decisions based on content.
Add toolbar buttons, floating panels, context menus, and other UI elements that integrate seamlessly with your editor.
Extension Anatomy
Let's break down what makes up a LexKit extension.
Method 1: createExtension Function
The functional approach - perfect for simple, focused extensions
Every extension starts with the createExtension function. This factory function creates a strongly typed extension instance with proper TypeScript support.
Basic Extension Structure
Define the actions your extension provides. Each command is a function that receives the Lexical editor instance and performs some operation.
Define functions that return information about the editor's current state. These are async functions that can read from the editor safely.
Try createExtension
Interactive demo of the functional approach. Click the buttons to test extension features!
createExtension Demo
Functional approach - simple and focused
Method 2: BaseExtension Class
The object-oriented approach - ideal for complex extensions with inheritance
The BaseExtension class provides a more traditional class-based approach to creating extensions. This is perfect for complex extensions that need inheritance or shared state.
Key Benefits:
- • Traditional class-based architecture
- • Instance methods and properties
- • Inheritance and method overriding
- • Better for complex state management
Implement the getCommands method to return your extension's command functions. Each command receives the Lexical editor instance and performs operations.
Implement the getStateQueries method to return async functions that query the editor's current state safely using editor.read().
Choose BaseExtension when you need:
Perfect For:
- • Complex state management
- • Multiple related commands
- • Inheritance hierarchies
- • Instance-specific configuration
Use createExtension For:
- • Simple, functional extensions
- • Quick prototyping
- • Single-purpose extensions
- • Less complex state needs
Try BaseExtension
Interactive demo of the class-based approach. Same functionality, different architecture!
BaseExtension Demo
Class-based approach - perfect for complex extensions
Using Your Extension
Universal Integration: No matter which approach you choose to create your extensions, the integration process is exactly the same. Both createExtension and BaseExtension extensions work identically in your editor system.
Create a const assertion array containing your extensions. The process is identical regardless of how you created your extensions - both approaches work the same way.
const extensions = [YourExtension] as const;
Use the createEditorSystem function to generate a typed Provider and useEditor hook based on your extensions. The process is identical for both approaches.
const { Provider, useEditor } = createEditorSystem<typeof extensions>();Wrap your editor with the Provider and use the useEditor hook to access commands and state queries from your extensions. Works the same way for both approaches.
Using Extension Commands
Best Practices
Tips for building maintainable and performant extensions.
Leverage TypeScript's power. Define clear interfaces for your commands and state queries. This prevents runtime errors and improves developer experience.
Always return a cleanup function from your initialize method. This ensures event listeners and other resources are properly disposed of.
Use editor.read() for reading operations and editor.update()for mutations. Avoid unnecessary re-renders and optimize state queries.
Document your extension's API clearly. Include JSDoc comments for commands and state queries to help other developers understand your extension.