Skip to content

How to compile the JS file to use a React component in a WordPress theme if I do not need SSR?

I have a WordPress theme and for a part of it I want to use a React component (I don’t need SSR). I have used create-react-app in the past but now I have this code:

<div id="react-nav-bar"></div>
<script src="" crossorigin></script>
<script src="" crossorigin></script>
<script src="<?php echo get_template_directory_uri() ?>/react-nav-bar/build/static/js/runtime-main.39639aca.js"></script>

and it does not work. The request to runtime-main.39639aca.js succeeds but it does not load my nav bar.

The issue is that create-react-app produces more than a JS file, and I need just a JS file that I can put in the theme.

I have tried some things with webpack without success. I searched on GitHub and there are some things, I do not know which one to try. I tried react-embedded and it threw me an error. I also tried this but it does not work for me.

I also dislike the ES Lint warnings (I use VS Code).

Update 1

This command does what I want, partially:

npx babel --watch react-nav-bar/src --out-dir react-nav-bar/dist --presets react-app/prod

This is how I’ve tried without WebPack, but the editor in VS Code is filled with red squiggles although it compiles well, and I cannot import npm modules. Is there a way to be able to import npm modules? Autocompletion makes me a lot more productive.


I gave Rollup a try and it works beautifully.

I get some warnings in the DevTools console but it works! With autocompletion and npm module includes.

Based on this gist I have these files:


import * as React from "react";
import { render } from 'react-dom';
const App = () => {
  return <button onClick={() => alert('React from WordPress !')}>Cool!!!</button>;
window.addEventListener('DOMContentLoaded', () => {
  render(<App />, document.getElementById('react-nav-bar'));


Just two additional scripts:

"build": "rollup -c",
"watch": "rollup -cw"

The unusual dependencies when compared with the gist linked above:

"@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-node-resolve": "^11.2.1",
"rollup-plugin-typescript": "^1.0.1",


import typescript from 'rollup-plugin-typescript';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
export default {
    input: './src/index.tsx',
    output: {
        file: './dist/bundle.js',
    format: 'iife',
    plugins: [ resolve(), typescript(), commonjs() ],


  "compilerOptions": {
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "forceConsistentCasingInFileNames": true,
    "jsx": "react",
    "module": "es6",
    "moduleResolution": "node",
    "noImplicitAny": true,
    "outDir": "./dist",
    "preserveConstEnums": true,
    "target": "es5",
    "allowSyntheticDefaultImports": true
  "exclude": [

For React to work with this I have the special line: "allowSyntheticDefaultImports": true.


<div id="react-nav-bar"></div>
<script>try { process.env.NODE_ENV } catch(e) { var process = { env: { NODE_ENV: 'production' } }; }</script>
<script src="<?php echo get_template_directory_uri() ?>/react-nav-bar/dist/bundle.js"></script>

This helped a lot: