Skip to main content

Introduction to the data layer

The WordPress data module (@wordpress/data) serves as a centralized hub for managing application state. It provides tools to manage data within and between distinct modules, designed to be simple enough for small plugins yet scalable for complex single-page applications.
The data module is built upon and shares many core principles with Redux, but includes several distinguishing characteristics that make it unique.

Installation

Install the data package:

Core architectural principle

The data layer follows a key architectural decision:
Data layer principle: Use @wordpress/data (Redux-like stores). Edit entities through core-data actions (editEntityRecord / saveEditedEntityRecord), not direct state manipulation.

Registering a store

Create and register a store using createReduxStore and register:

Store configuration options

Reducer

A reducer is a function that accepts the previous state and action and returns an updated state value:
Reducers must be pure functions—no side effects, no API calls, just state transformations.

Actions

The actions object describes all action creators available for your store:
Dispatching actions is the primary mechanism for making changes to your state.

Selectors

The selectors object includes functions for accessing and deriving state values:
Calling selectors is the primary mechanism for retrieving data from your state. They provide abstraction over raw data that’s typically more susceptible to change.

Resolvers

A resolver is a side-effect for a selector. If your selector result needs fulfillment from an external source, define a resolver:
Resolvers:
  • Execute the first time a selector is called
  • Receive the same arguments as the selector (excluding state)
  • Can dispatch actions to fulfill selector requirements
  • Work with thunks for asynchronous data flows

Using stores with React hooks

useSelect - Reading data

Retrieve data from stores using useSelect:
With dependencies:
The dependencies array ([ currency ]) ensures the selector only re-runs when those values change.

useDispatch - Writing data

Dispatch actions using useDispatch:

useRegistry - Accessing the registry

Access the registry directly for advanced use cases:

Higher-order components

withSelect - Injecting data

withDispatch - Injecting actions

Core WordPress data stores

WordPress provides several built-in stores:
  • core/blocks - Registered block types and block collections
  • core/block-editor - Block editor state (selected blocks, settings, preferences)
  • core/editor - Post editor state (current post, saving status, etc.)
  • core (core-data) - WordPress entities via REST API (posts, pages, media, etc.)
  • core/notices - User-facing notices and snackbars
  • core/viewport - Responsive breakpoints and device information

Working with core-data

The core store (core-data) manages WordPress entities:
Always use editEntityRecord and saveEditedEntityRecord for editing entities, not direct state manipulation.

Batching updates

Use registry.batch() to batch multiple store updates:
Batching is particularly effective for expensive selectors, atomic operations across stores, and creating single undo/redo entries.

Resolution state selectors

Track resolver execution state:

Registry selectors

Create selectors that access other stores:
Registry selectors can call selectors from other stores, enabling cross-store data derivation.

Comparison with Redux

The data module shares Redux core principles but differs in:

Similarities

  • Unidirectional data flow
  • Pure reducer functions
  • Action-based state updates
  • Selector-based data access

Differences

  1. Modular stores: Separate but interdependent stores instead of a single global store
  2. Selectors as primary API: Selectors are the main entry point for data access
  3. Built-in async handling: Resolvers and thunks for asynchronous operations
  4. Subscribe optimizations: Subscribers only called when state actually changes
  5. Split HOCs: withSelect and withDispatch instead of single connect

Async data flows

Handle asynchronous operations with thunks:
Thunks receive dispatch, select, and registry as arguments, enabling complex async workflows.

Generic stores

Integrate existing Redux stores or create completely custom stores:

Best practices

  1. Use selectors for all data access - Never access state directly
  2. Keep reducers pure - No side effects, API calls, or mutations
  3. Use resolvers for data fetching - Let resolvers handle async operations
  4. Batch related updates - Use registry.batch() for multiple dispatches
  5. Memoize expensive selectors - Use createSelector for computed data
  6. Define clear dependencies - Always specify dependency arrays in hooks
  7. Edit entities correctly - Use editEntityRecord for core-data entities
  8. Track resolution state - Use resolution selectors for loading states

Next steps