Skip to main content
We want to make contributing to Lexical as easy and transparent as possible. Whether you’re fixing bugs, adding features, or improving documentation, your contributions are welcome!

Getting Started

Prerequisites

Lexical uses pnpm as its package manager. Install it if you haven’t already:
npm install -g pnpm
# or use corepack (recommended for all Node.js versions)
npx corepack@latest enable

Development Setup

  1. Fork the repository on GitHub
  2. Clone your fork:
    git clone https://github.com/YOUR_USERNAME/lexical.git
    cd lexical
    
  3. Install dependencies:
    pnpm install
    
  4. Start the development server:
    pnpm run start
    
    This starts both the playground dev server and collab server at http://localhost:3000
If you don’t need collaboration features, use pnpm run dev to start just the dev server.

Making Changes

Pull Request Process

  1. Create a branch from main:
    git checkout -b my-feature-branch
    
  2. Make your changes following our coding standards
  3. Add tests if you’ve added code that should be tested
  4. Update documentation if you’ve changed APIs
  5. Ensure the test suite passes:
    pnpm run test-unit
    
  6. Make sure your code lints:
    pnpm run ci-check
    
  7. Commit your changes with a clear commit message
  8. Push to your fork and submit a pull request

Running Tests

Unit Tests

# Run all unit tests
pnpm run test-unit

# Run in watch mode
pnpm run test-unit-watch

# Debug tests
pnpm run debug-test-unit

End-to-End Tests

E2E tests require the local server to be running.
# Terminal 1: Start the dev server
pnpm run start

# Terminal 2: Run E2E tests
pnpm run test-e2e-chromium
pnpm run test-e2e-firefox
pnpm run test-e2e-webkit

# Debug E2E tests (headed mode)
pnpm run debug-test-e2e-chromium

Code Quality

Before submitting, run all checks:
# Run all checks (TypeScript, Flow, Prettier, ESLint)
pnpm run ci-check

# Individual checks
pnpm run tsc          # TypeScript
pnpm run flow         # Flow type checker
pnpm run lint         # ESLint
pnpm run prettier     # Check formatting
pnpm run prettier:fix # Auto-fix formatting

Contributor License Agreement (CLA)

Before we can accept your pull request, you need to submit a CLA. You only need to do this once for any Meta open source project. Complete your CLA here: https://code.facebook.com/cla

Documentation Contributions

To work on the documentation website:
# Start Docusaurus dev server
pnpm run start:website

# Build the website
pnpm -C packages/lexical-website run build
The documentation is located in packages/lexical-website/.

Reporting Issues

Bug Reports

When reporting bugs, please include:
  • Clear description of the issue
  • Steps to reproduce
  • Expected vs actual behavior
  • Lexical version
  • Browser/environment details
  • Minimal reproduction example (CodeSandbox, StackBlitz, etc.)
Use GitHub Issues to track public bugs.

Security Issues

Meta has a bounty program for security bugs. Please go through that process instead of filing a public issue.

Development Workflow

Build Commands

# Development build
pnpm run build

# Production build
pnpm run build-prod

# Build with error codes (release)
pnpm run build-release

# Build TypeScript types
pnpm run build-types

Project Structure

Lexical is a monorepo with packages in packages/:
  • lexical - Core framework
  • @lexical/react - React bindings
  • @lexical/rich-text - Rich text features
  • @lexical/plain-text - Plain text features
  • @lexical/list - List nodes
  • @lexical/table - Table support
  • @lexical/markdown - Markdown support
  • @lexical/history - Undo/redo
  • @lexical/yjs - Collaboration via Yjs
  • And many more…

Type Systems

Lexical uses both TypeScript and Flow. When modifying APIs, ensure types are correct for both systems.
# Check TypeScript types
pnpm run tsc

# Check Flow types  
pnpm run flow

Coding Guidelines

General Principles

  • Write clear, self-documenting code
  • Add comments for complex logic
  • Follow existing code style
  • Keep changes focused and atomic
  • Write tests for new features
  • Update documentation for API changes

$ Function Convention

Functions prefixed with $ (e.g., $getRoot(), $getSelection()) can only be called within:
  • editor.update(() => {...}) - for mutations
  • editor.read(() => {...}) - for read-only access
  • Node transforms and command handlers
This enforces proper update context similar to React hooks.

Custom Nodes

When creating custom nodes:
  1. Extend a base class (TextNode, ElementNode, DecoratorNode)
  2. Implement required static methods: getType(), clone(), importJSON()
  3. Implement instance methods: createDOM(), updateDOM(), exportJSON()
  4. Register with editor config: nodes: [YourCustomNode]
  5. Export a $createYourNode() factory function

Community

Discord

Join our Discord for questions and discussions

Twitter

Follow @lexicaljs for updates and announcements

GitHub Issues

Report bugs and request features

GitHub Discussions

Ask questions and share ideas

License

By contributing to Lexical, you agree that your contributions will be licensed under the MIT License.

Recognition

All contributors are recognized in our Contributors Graph. Thank you for helping make Lexical better!

Build docs developers (and LLMs) love