Written by 2:16 am React & Vue for CMS Views: 1

React in WordPress: Building Interactive Blocks and Admin Interfaces

How React actually works inside WordPress, building Gutenberg blocks with @wordpress/scripts, creating admin screens with @wordpress/components, state management with data stores, and when to reach for the Interactivity API instead.

Banner for React in WordPress building interactive blocks and admin interfaces

WordPress has shipped React since 2018, but nobody talks about it like that

WordPress ships React. It has since Gutenberg landed in version 5.0 in late 2018. But the React you use inside WordPress is not quite the React you learned on a tutorial site. It is wrapped in @wordpress/element, it talks to @wordpress/data instead of Redux directly, and its build tooling is one command (wp-scripts start) rather than a webpack config you wrote yourself. Once you learn the WordPress conventions on top of the underlying React, WordPress plus React is genuinely nice to work with.

This guide covers the three places React actually shows up in real WordPress development: custom Gutenberg blocks, admin screens for settings or custom dashboards, and interactive frontend UI via the Interactivity API. I will not re-explain React itself because the official React documentation does that better than I could. My focus is specifically where WordPress wraps, renames, or replaces what you already know about vanilla React.

The WordPress React stack, renamed

You import from @wordpress/* packages instead of directly from react. This is the first thing that confuses developers coming from a Create React App background. The reason is that WordPress wraps React in its own reconciler to handle the quirks of the block editor environment, and the wrapped version is what ships with every WordPress install. If you import from react directly, your plugin pulls in a second copy of React, which breaks in subtle ways around context and refs.

Here is the package map for anyone new to the WordPress React ecosystem:

  • @wordpress/element is React with WordPress’s internal reconciler. createElement, Fragment, and all the hooks work the same as vanilla React.
  • @wordpress/components is the WordPress-styled UI component library. Button, Modal, SelectControl, PanelBody, and about 80 other primitives. Use these before you reach for Material UI or Chakra.
  • @wordpress/block-editor is the editor-specific component collection: InspectorControls, BlockControls, RichText, MediaUpload. These only work inside the block editor context.
  • @wordpress/data is WordPress’s Redux wrapper with registered stores. Every core WordPress feature has a store (core/block-editor, core/editor, core), and your own plugins can register additional stores.
  • @wordpress/i18n is the internationalization layer. __(), _n(), sprintf(). Use these for every user-visible string.

The build tool is @wordpress/scripts. Add it as a dev dependency, add "start": "wp-scripts start" and "build": "wp-scripts build" to package.json, and you have hot reload, Babel, and webpack pre-configured. Never hand-roll this. The configuration that ships with @wordpress/scripts matches WordPress core’s expectations and is updated in lockstep with WordPress releases. Rolling your own webpack config is a recipe for breaking every six months when core changes something.

Building a Gutenberg block

A modern block is one folder with four files. The folder structure is opinionated and the tooling depends on it, so do not restructure without reason.

The block.json file is the source of truth for block metadata. WordPress reads this file to register the block, discover its attributes, and load the editor and frontend scripts. Here is a minimal example for a “team member” block that shows a name, role, and image:

Three things about block.json to internalize before you start writing blocks. First, apiVersion: 3 is the current default in 2026. Older blocks used version 1 or 2, and they still work, but do not start new blocks on anything older than 3. Second, the supports object is how you get core features for free. Declaring color.background: true enables the color picker in the sidebar without any React code on your side. This is the single biggest productivity win in block development. Third, the file: prefixes tell WordPress to look for those assets relative to block.json. Never hand-register scripts separately if block.json can do it for you.

The edit.js file is the React component rendered inside the editor. This is where most of the actual React work lives. Here is what a simple edit component looks like:

Three things to note. First, useBlockProps handles the wrapper div className, alignment, and editor highlighting automatically. Always use it. Skipping useBlockProps is how you end up with blocks that look wrong in the editor compared to the frontend. Second, InspectorControls renders into the sidebar, and BlockControls renders into the floating toolbar. Use each for its appropriate purpose. Third, RichText gives you inline formatting for free. Never use a plain input for block text content, because RichText handles bold, italic, and links that editors expect to work.

Register the block from PHP with register_block_type( __DIR__ . '/build/my-block' ); after running wp-scripts build. The PHP side is a single function call. All the real logic lives in the JavaScript.

Admin screens with React

For plugin settings pages, custom dashboards, or any admin-facing tool, React is a better fit than the old Settings API once the UI gets even modestly complex. The pattern is: register a PHP admin page that renders an empty <div id="my-admin-root"></div>, then mount a React app into it.

The PHP side looks like this:

The key detail is the $asset file. When you run wp-scripts build, it generates a build/admin.asset.php file containing the correct dependency list and a cache-busting version hash. Always use it instead of hand-listing @wordpress/* scripts, because the dependency list is generated from your actual imports and stays accurate across WordPress versions.

Inside admin.js, the React app looks like this:

The apiFetch helper auto-injects the REST API nonce, which is why you use it instead of plain fetch. On the PHP side, you have to register the REST route with a proper permission_callback. This is non-negotiable, and returning true from permission_callback is both a security bug and a WPCS violation. See my AI code review guide for the full permission callback audit process.

State management with @wordpress/data

For anything more complex than a couple of useState calls, register a Redux-style store. @wordpress/data is Redux with less boilerplate, and the API is genuinely pleasant once you learn the pattern:

In a component, read with useSelect and write with useDispatch. The hooks handle subscription and re-rendering automatically, so you do not have to think about it. If you have used Redux Toolkit, this will feel familiar but slightly lighter.

When to reach for the Interactivity API instead

React inside WordPress is great for the editor and for admin screens. It is not great for frontend UI that needs to ship in a public theme. Hydrating React on the frontend bloats page weight, competes with the page cache, and introduces a hydration mismatch risk that is genuinely hard to debug.

The Interactivity API, which ships with WordPress via data-wp-* directives and the @wordpress/interactivity package, is WordPress’s frontend story. It has a small runtime, works with page caching out of the box, and feels like Alpine.js or Petite Vue. If a block needs an accordion, a tabs widget, a light/dark toggle, or a dismissible alert on the frontend, use the Interactivity API, not React components rendered via save.js.

The simple rule I use: editor UI, admin UI, and complex state go to React. Frontend interactivity on a shipped block goes to the Interactivity API. If you follow that rule, you will avoid about 90 percent of the weird performance problems that WordPress React plugins run into.

Common mistakes I see in plugin code reviews

Five mistakes I flag on almost every React-in-WordPress plugin I review:

  • Importing from react instead of @wordpress/element. Works until it does not, and when it breaks, the error message is cryptic.
  • Hand-listing wp-element and wp-components in wp_enqueue_script dependencies instead of using the generated .asset.php. The dependency list goes stale and your plugin breaks when WordPress reorganizes internal packages.
  • Forgetting permission_callback on the REST route the admin app calls. This is the number one security issue in WordPress React plugins.
  • Building a settings UI in React when a 50-line Settings API page would have done the job. React is a tool, not a default. For simple settings pages, use the boring PHP.
  • Not using the Interactivity API for frontend blocks. This is the newer mistake, but it will become the most common one over the next two years.

Next steps for WordPress React development

If you are new to WordPress plus React, start by copying the official scaffold with npx @wordpress/create-block and reading its output line by line. It is the shortest path to understanding the full toolchain. Once you have shipped one block and one admin screen, the rest is pattern-matching against the same underlying concepts.

The tooling is also evolving. WordPress is rolling out @wordpress/build as the successor to @wordpress/scripts, with faster builds, native TypeScript, and improved hot module replacement. See my guide to the next-gen WordPress plugin build tool for what is changing and how to migrate when the new tool stabilizes later this year.

Last modified: April 14, 2026

Close