Skip to main content
Performance is crucial for editor applications. The Block Editor monitors key metrics and implements strategies to maintain responsiveness as content grows.

Key Metrics

Three primary metrics are tracked:

Loading Time

The time from page request to first block render:
  • Server response time
  • Time to first paint (FP)
  • Time to first contentful paint (FCP)
  • DOM content load complete
  • Load complete
  • First block render

Typing Time

Browser response time while typing in the editor. This measures the delay between keypress and visual update.

Block Selection Time

Response time when selecting or inserting blocks. Since inserting a block is equivalent to selecting it, this metric covers both operations.
Monitor these metrics during development to catch performance regressions early.

Data Module Async Mode

The Block Editor uses Redux for state management through the @wordpress/data package. As the number of blocks grows, global state updates can cause performance issues.

The Problem

With synchronous rendering:
  • Every state change triggers updates to all subscribed components
  • Long posts with many blocks cause UI lag
  • Typing becomes sluggish as component count increases

The Solution

Async Mode enables selective synchronous rendering:
// Selected block renders synchronously for instant feedback
<BlockListBlock clientId={ selectedBlock } isSynchronous={ true } />

// Other blocks render asynchronously when browser is idle
<BlockListBlock clientId={ otherBlock } isSynchronous={ false } />
How it works:
  1. The selected block updates synchronously for immediate response
  2. All other blocks update asynchronously during browser idle time
  3. Editor stays responsive regardless of content length
This optimization is based on the principle that editing one block rarely affects others, so most updates can be deferred.

Performance Testing

Running Benchmarks Locally

Compare performance across branches:
./bin/plugin/cli.js perf trunk v8.1.0 v8.0.0
The tool:
  1. Prepares test environments for each branch
  2. Runs performance tests in each environment
  3. Computes medians for all metrics
  4. Generates a comparison report

Test Environment Structure

├── tests/packages/e2e-tests/specs/performance/*
│   Performance test suites

├── tests/test/emptytheme
│   Theme for test environment

├── envs/branch1/.wp-env.json
│   Environment config for branch1

├── envs/branch1/plugin
│   Built Gutenberg plugin for branch1

└── envs/branchX
    Additional branch environments

Test Process

For each branch:
  1. Start the wp-env environment
  2. Run performance test suite
  3. Stop the environment
  4. Record results
  5. Repeat for remaining branches
  6. Calculate and display medians
Use identical test and environment versions across branches. Only the Gutenberg plugin version should differ.

CodeVitals Tracking

Performance results are tracked on CodeVitals for the Gutenberg project.

Handling CI Variability

GitHub CI resources vary between runs. To ensure consistent tracking:
  • Each trunk commit is compared to a fixed reference commit
  • Relative differences remain consistent despite environment changes
  • Trends are reliable even with varying absolute numbers

Updating the Reference Commit

Update when WordPress version requirements change:
# .github/workflows/performance.yml
reference-commit: 'abc123'
The reference commit must:
  • Be compatible with the new WordPress version
  • Already be tracked on CodeVitals for all metrics
  • Have a passing performance job
Choose a recent trunk commit with passing tests as your reference commit.

Optimization Strategies

Reduce Component Re-renders

Use selectors efficiently:
import { useSelect } from '@wordpress/data';
import { store as blockEditorStore } from '@wordpress/block-editor';

// Bad: Selects entire block object, causes re-render on any change
const block = useSelect(
  ( select ) => select( blockEditorStore ).getBlock( clientId ),
  [ clientId ]
);

// Good: Select only needed properties
const { blockName, attributes } = useSelect(
  ( select ) => {
    const block = select( blockEditorStore ).getBlock( clientId );
    return {
      blockName: block?.name,
      attributes: block?.attributes,
    };
  },
  [ clientId ]
);

Memoize Expensive Calculations

import { useMemo } from 'react';

function BlockEdit( { attributes } ) {
  // Expensive calculation only runs when attributes change
  const processedData = useMemo(
    () => expensiveOperation( attributes ),
    [ attributes ]
  );
  
  return <div>{ processedData }</div>;
}

Debounce User Input

import { useDebounce } from '@wordpress/compose';

function SearchControl() {
  const [ searchTerm, setSearchTerm ] = useState( '' );
  
  // Debounce expensive search operation
  const debouncedSearch = useDebounce(
    ( value ) => performSearch( value ),
    500
  );
  
  const handleChange = ( value ) => {
    setSearchTerm( value );
    debouncedSearch( value );
  };
}

Lazy Load Heavy Components

import { Suspense, lazy } from 'react';

const HeavyComponent = lazy( () => import( './HeavyComponent' ) );

function MyBlock() {
  return (
    <Suspense fallback={ <Spinner /> }>
      <HeavyComponent />
    </Suspense>
  );
}

Best Practices

  • Profile performance before and after changes
  • Use browser DevTools Performance tab to identify bottlenecks
  • Minimize selector dependencies in useSelect
  • Avoid anonymous functions in render methods
  • Use React.memo for components that rarely change
  • Keep component trees shallow

Measuring Performance

import { useEffect } from 'react';

function MyComponent() {
  useEffect( () => {
    const startTime = performance.now();
    
    // Perform operation
    expensiveOperation();
    
    const duration = performance.now() - startTime;
    console.log( `Operation took ${ duration }ms` );
  }, [] );
}

React DevTools Profiler

Use the Profiler to identify slow components:
  1. Open React DevTools
  2. Switch to Profiler tab
  3. Click record
  4. Perform actions in editor
  5. Stop recording
  6. Analyze component render times

Additional Resources

Build docs developers (and LLMs) love