<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nima Fadaei</title>
    <description>The latest articles on DEV Community by Nima Fadaei (@nimafadaei).</description>
    <link>https://dev.to/nimafadaei</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2239193%2Fb54fbc90-af8e-4dea-aea2-6bd0c7ad5ca2.png</url>
      <title>DEV Community: Nima Fadaei</title>
      <link>https://dev.to/nimafadaei</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nimafadaei"/>
    <language>en</language>
    <item>
      <title>Building Custom Elementor Widgets with React &amp; the WordPress REST API — a Practical Guide</title>
      <dc:creator>Nima Fadaei</dc:creator>
      <pubDate>Wed, 22 Oct 2025 18:48:07 +0000</pubDate>
      <link>https://dev.to/nimafadaei/building-custom-elementor-widgets-with-react-the-wordpress-rest-api-a-practical-guide-3m2c</link>
      <guid>https://dev.to/nimafadaei/building-custom-elementor-widgets-with-react-the-wordpress-rest-api-a-practical-guide-3m2c</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Elementor is one of the most powerful page builders in the WordPress ecosystem. While its built-in widgets cover most design needs, creating custom widgets allows developers to craft truly unique, data-driven, and interactive user experiences.&lt;/p&gt;

&lt;p&gt;In this post, we’ll walk through how to build a custom Elementor widget that uses React for the frontend, PHP for registration and logic, and the WordPress REST API for dynamic data fetching.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You’ll learn how to:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Scaffold a lightweight WordPress plugin structure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build the widget interface with React.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fetch real WordPress data using the REST API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Properly enqueue and localize assets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Package and deploy your widget like a pro.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What You’ll Need&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before you begin, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;WordPress (latest version)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Elementor (free or Pro)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Node.js &amp;amp; npm installed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Basic knowledge of PHP, JavaScript, and React&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Plugin Structure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a folder called elementor-react-widget and add the main plugin file elementor-react-widget.php:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
/**
 * Plugin Name: Elementor React Widget
 * Description: A custom Elementor widget powered by React and the WP REST API.
 * Version: 1.0.0
 * Author: Nima Fadai
 */

if ( ! defined( 'ABSPATH' ) ) exit;

final class ERW_Plugin {
    const VERSION = '1.0.0';
    const SLUG = 'erw';

    public function __construct() {
        add_action( 'init', [ $this, 'register_assets' ] );
        add_action( 'elementor/widgets/register', [ $this, 'register_widget' ] );
    }

    public function register_assets() {
        $dir = plugin_dir_url( __FILE__ );
        wp_register_script(
            self::SLUG . '-app',
            $dir . 'build/index.js',
            [ 'wp-element', 'wp-api' ],
            self::VERSION,
            true
        );

        wp_localize_script( self::SLUG . '-app', 'ERW_Settings', [
            'rest_url' =&amp;gt; esc_url_raw( rest_url() ),
            'nonce'    =&amp;gt; wp_create_nonce( 'wp_rest' ),
        ]);

        wp_register_style( self::SLUG . '-styles', $dir . 'build/styles.css', [], self::VERSION );
    }

    public function register_widget( $widgets_manager ) {
        require_once __DIR__ . '/widgets/class-erw-widget.php';
        $widgets_manager-&amp;gt;register( new \ERW_Widget() );
    }
}

new ERW_Plugin();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This PHP file registers scripts, styles, and your Elementor widget using WordPress hooks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: The Widget Class&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inside a new folder widgets/, create the file class-erw-widget.php:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
if ( ! defined( 'ABSPATH' ) ) exit;

use Elementor\Widget_Base;
use Elementor\Controls_Manager;

class ERW_Widget extends Widget_Base {
    public function get_name() {
        return 'erw_react_widget';
    }

    public function get_title() {
        return 'React Elementor Widget';
    }

    public function get_icon() {
        return 'eicon-code';
    }

    public function get_categories() {
        return [ 'general' ];
    }

    protected function register_controls() {
        $this-&amp;gt;start_controls_section('content_section', [
            'label' =&amp;gt; 'Content',
            'tab' =&amp;gt; Controls_Manager::TAB_CONTENT,
        ]);

        $this-&amp;gt;add_control('title', [
            'label' =&amp;gt; 'Title',
            'type' =&amp;gt; Controls_Manager::TEXT,
            'default' =&amp;gt; 'Hello from React Widget',
        ]);

        $this-&amp;gt;end_controls_section();
    }

    protected function render() {
        $settings = $this-&amp;gt;get_settings_for_display();
        echo '&amp;lt;div class="erw-root" data-title="' . esc_attr( $settings['title'] ) . '"&amp;gt;&amp;lt;/div&amp;gt;';
        wp_enqueue_script( 'erw-app' );
        wp_enqueue_style( 'erw-styles' );
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This class defines the Elementor widget, adds a basic text control, and renders a &lt;/p&gt; for React to mount into.

&lt;p&gt;&lt;strong&gt;Step 3: The React App&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inside your plugin folder, create a src/ directory and a file src/index.js:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';
import { createRoot } from 'react-dom/client';

function App({ mountEl }) {
  const title = mountEl.dataset.title || 'Default Title';
  const [posts, setPosts] = React.useState([]);
  const [loading, setLoading] = React.useState(true);

  React.useEffect(() =&amp;gt; {
    async function fetchPosts() {
      try {
        const response = await fetch(`${ERW_Settings.rest_url}wp/v2/posts?per_page=5`, {
          headers: { 'X-WP-Nonce': ERW_Settings.nonce }
        });
        const data = await response.json();
        setPosts(data);
      } catch (err) {
        console.error('Error fetching posts:', err);
      } finally {
        setLoading(false);
      }
    }
    fetchPosts();
  }, []);

  return (
    &amp;lt;div className="erw-widget"&amp;gt;
      &amp;lt;h3&amp;gt;{title}&amp;lt;/h3&amp;gt;
      {loading ? &amp;lt;p&amp;gt;Loading...&amp;lt;/p&amp;gt; : (
        &amp;lt;ul&amp;gt;
          {posts.map(post =&amp;gt; (
            &amp;lt;li key={post.id}&amp;gt;
              &amp;lt;a href={post.link}&amp;gt;{post.title.rendered}&amp;lt;/a&amp;gt;
            &amp;lt;/li&amp;gt;
          ))}
        &amp;lt;/ul&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  );
}

document.addEventListener('DOMContentLoaded', () =&amp;gt; {
  const roots = document.querySelectorAll('.erw-root');
  roots.forEach(el =&amp;gt; {
    const root = createRoot(el);
    root.render(&amp;lt;App mountEl={el} /&amp;gt;);
  });
});

&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;This code dynamically fetches recent WordPress posts and displays them inside your Elementor widget.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Build the React Bundle&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can use Vite, Webpack, or any preferred bundler.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example setup with Vite:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm create vite@latest elementor-react-widget --template react

&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Then adjust the build.outDir in vite.config.js to ../build, so your compiled JS lands in your plugin’s build folder.&lt;br&gt;
Finally run:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run build

&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;&lt;strong&gt;Step 5: Security &amp;amp; Optimization Tips&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Always use wp_create_nonce() for API security.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Minify JS and CSS in production.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use lazy loading for large data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Avoid global namespace conflicts by prefixing your functions and scripts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cache REST API responses where possible.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 6: Packaging &amp;amp; Testing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Add a proper readme.txt file with plugin information.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Test it inside Elementor’s editor to confirm compatibility.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Document any dependencies or build steps.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once confirmed, your plugin is production-ready — you’ve just built a React-powered Elementor widget connected to live WordPress data!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Combining React, WordPress, and Elementor creates limitless opportunities for web innovation.&lt;br&gt;
By separating logic (PHP) from the UI (React), you gain scalability, flexibility, and a modern workflow that aligns with today’s front-end development standards.&lt;/p&gt;

&lt;p&gt;Whether you’re building internal tools, client dashboards, or interactive UI blocks — this hybrid approach bridges the gap between WordPress tradition and modern web technology.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FAQs&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;1. Why use React for Elementor widgets?&lt;/strong&gt;&lt;br&gt;
React provides better state management, reactivity, and scalability for dynamic UIs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Does this affect Elementor performance?&lt;/strong&gt;&lt;br&gt;
No — as long as you properly enqueue and scope scripts, your widget will run smoothly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Can I integrate custom REST endpoints?&lt;/strong&gt;&lt;br&gt;
Yes. You can register custom routes with register_rest_route() in PHP and consume them in React.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Is this compatible with Elementor Pro?&lt;/strong&gt;&lt;br&gt;
Absolutely. It behaves like any native widget and can coexist with Pro features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. How do I optimize the build size?&lt;/strong&gt;&lt;br&gt;
Use production mode builds, tree-shaking, and CDN caching for static files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Can I extend this widget for client dashboards?&lt;/strong&gt;&lt;br&gt;
Definitely — just modify the REST endpoint or connect to external APIs.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>css</category>
      <category>php</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
