Vite CORS Configuration Fix

User
Access to script at 'http://localhost:5173/src/main.jsx' from origin 'https://wprock.ddev.site' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Assistant
Got it — you’re facing this CORS error:

Access to script at 'http://localhost:5173/src/main.jsx' from origin 'https://wprock.ddev.site' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

**What's happening:**
You're running Vite dev server (`localhost:5173`) but trying to access it from another domain (`https://wprock.ddev.site`).
Browsers block this by default unless CORS headers (`Access-Control-Allow-Origin`) are properly sent.

### How to fix it:

✅ You **must tell Vite to allow CORS from your `.ddev.site` domain**.
In your **Vite config (`vite.config.js`)**, add this:

export default {
  server: {
    host: '0.0.0.0', // allow external access
    port: 5173,
    cors: true,
    headers: {
      'Access-Control-Allow-Origin': '*', // Or more strictly: 'https://wprock.ddev.site'
    },
    strictPort: true,
    hmr: {
      host: 'wprock.ddev.site',
      protocol: 'wss', // if you're using https on ddev
    },
  },
}

👉 **Important notes**:
– Use `wss` if your DDEV site is HTTPS (which it is: `https://wprock.ddev.site`).
– Setting `host: '0.0.0.0'` makes Vite accessible from the outside (your DDEV domain can see it).
– `headers['Access-Control-Allow-Origin']` allows CORS.
– `strictPort: true` is optional but helps prevent random port changes.

### After editing `vite.config.js`:
1. Restart your Vite server:

yarn dev
   # or
   npm run dev

2. Clear browser cache if needed (especially if Chrome cached the CORS block).

### Extra tip:
If DDEV is HTTPS but Vite is only HTTP (`http://localhost:5173`), it may also give **"mixed content"** errors later (after fixing CORS).
In that case, you might also need to:
– **Trust certificates** or
– **Use a self-signed HTTPS dev server for Vite** (if needed, I can show you that too).

Would you like me to also give you a fully ready **example vite.config.js** based on your DDEV + React + WordPress setup? 🚀
(Only if you want to speed things up.)

User
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
import { fileURLToPath } from 'url'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

export default defineConfig({
base: '', // for WordPress theme builds
plugins: [react()],
build: {
outDir: 'dist',
assetsDir: 'assets',
manifest: true,
emptyOutDir: true,
rollupOptions: {
input: path.resolve(__dirname, 'src/main.jsx'),
},
},
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
server: {
host: '0.0.0.0', // expose to all interfaces
port: 5173,
strictPort: true,
origin: 'http://localhost:5173',
cors: true,
headers: {
'Access-Control-Allow-Origin': '*', // or use your exact domain
},
hmr: {
protocol: 'ws',
host: 'localhost',
port: 5173,
},
},
})

Assistant
Thanks for sharing your `vite.config.js`!
You’re **very close**, but **one small thing** is causing your CORS issue when accessing from `https://wprock.ddev.site`:

**Problem:**

hmr: {
-   protocol: 'ws',
-   host: 'localhost',
-   port: 5173,
},

You have `host: 'localhost'`, but **your browser page is running on `wprock.ddev.site`**, not on localhost!
That's why WebSocket and CORS both fail.

✅ **Correct HMR settings for your setup:**

hmr: {
+   protocol: 'wss', // if your ddev site is https
+   host: 'wprock.ddev.site',
+   port: 5173,
},

✔️ And keep `headers: { 'Access-Control-Allow-Origin': '*' }` for CORS.
✔️ And `host: '0.0.0.0'` is already correct.

### Full Corrected Vite Config for DDEV + WordPress (HTTPS)

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
import { fileURLToPath } from 'url'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

export default defineConfig({
  base: '',
  plugins: [react()],
  build: {
    outDir: 'dist',
    assetsDir: 'assets',
    manifest: true,
    emptyOutDir: true,
    rollupOptions: {
      input: path.resolve(__dirname, 'src/main.jsx'),
    },
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
    },
  },
  server: {
    host: '0.0.0.0',
    port: 5173,
    strictPort: true,
    cors: true,
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
    origin: 'https://wprock.ddev.site', // optional but better
    hmr: {
      protocol: 'wss', // HTTPS sites need secure websocket
      host: 'wprock.ddev.site',
      port: 5173,
    },
  },
})

### After this:

– Restart Vite (`yarn dev` or `npm run dev`).
– Visit your `https://wprock.ddev.site` site — now it should load Vite scripts fine without CORS issues 🚀.

### Why change `ws` → `wss`?
Because `wprock.ddev.site` is HTTPS.
If Vite HMR connects with an insecure `ws://`, browser **blocks it** for security reasons.

Would you also like me to show you a simple **proxy setup** so you don't have to open port 5173 separately next time? (even cleaner setup 🚀)
(Let me know if you want!)

User
but we are using react vite as a wordpress theme
Assistant
Good, thanks for clarifying!
Yes, you are **using React + Vite as a WordPress theme** — **inside DDEV** — and not running it separately like a typical Vite app.

That's even more reason why **fixing the Vite dev server settings properly** is important.

### ✅ Let me quickly break it down for your exact case:

**Right now your flow is:**

| Environment | Purpose |
|————————–|———————————————-|
| Vite dev server (localhost:5173) | Serves React assets live (hot reload) |
| DDEV WordPress (https://wprock.ddev.site) | Main site loads WordPress + theme (React assets injected) |

When you build for production (`yarn build`), Vite will generate `/dist` files and WordPress will serve them normally — **no CORS issues** at all.

But during **development**,
– WordPress theme template (PHP) tries to load assets from `http://localhost:5173/src/main.jsx`.
– Browser **blocks it** because of CORS + protocol mismatch (`https` vs `http`).

### 🔥 Therefore, **for WordPress theme using Vite dev server**, the correct setup is:

**In `vite.config.js`:**

server: {
  host: '0.0.0.0',              // Allow DDEV containers to access
  port: 5173,
  strictPort: true,
  cors: true,
  headers: {
    'Access-Control-Allow-Origin': '*', // or just 'https://wprock.ddev.site'
  },
  origin: 'https://wprock.ddev.site',    // Important: match your WordPress site URL
  hmr: {
    protocol: 'wss',           // Secure WebSocket for HTTPS WordPress
    host: 'wprock.ddev.site',   // Use the DDEV domain
    port: 5173,
  },
},

✅ This way WordPress can **pull Vite scripts directly during dev** without CORS failure.
✅ Vite HMR (`Hot Module Reload`) also works correctly (even inside WordPress).

### 🛠️ Also make sure in WordPress PHP side:

When enqueuing Vite assets in your theme (like `wp_enqueue_script`),
you dynamically check if it's **development** or **production**.

Example PHP check:

<?php
// functions.php or similar
$is_dev = str_contains($_SERVER['HTTP_HOST'], '.ddev.site') && file_exists(get_theme_file_path('vite.config.js'));

if ($is_dev) {
    // Load from Vite dev server
    wp_enqueue_script('theme-main', 'https://wprock.ddev.site:5173/src/main.jsx', array(), null, true);
} else {
    // Load from built /dist folder
    $manifest = json_decode(file_get_contents(get_theme_file_path('dist/manifest.json')), true);
    $main = $manifest['src/main.jsx']['file'];
    wp_enqueue_script('theme-main', get_template_directory_uri() . '/dist/' . $main, array(), null, true);
}
?>

This way:
– In development, load live from Vite (`localhost:5173` via DDEV URL).
– In production, load from `/dist` built files.

### 🔥 To summarize:

| Step | Status |
|————————-|—————-|
| Fix Vite CORS + HMR ✅ | Yes |
| Make Vite accessible inside DDEV ✅ | Yes |
| WordPress dynamically loads dev vs prod assets ✅ | Yes |
| Build production bundle (`yarn build`) later ✅ | Yes |

### 🚀 **Final Result** after this:

– `npm run dev` → automatic hot reloading inside WordPress theme
– `yarn build` → production-ready `/dist` assets for real WordPress usage
– No CORS issues
– No HMR failures

Would you like me to also give you a sample **WordPress + React Vite theme structure**? (like what goes inside `src/`, `dist/`, `functions.php`, etc.)
If yes, I can prepare a quick blueprint for you! 🔥
Would be very helpful if you're planning a full production move later.

User
but now wprock.ddev.site not showing website
Uncaught Error: @vitejs/plugin-react can't detect preamble. Something is wrong.
at Home.jsx:9:5
Assistant
Alright — now we’re getting super close 🔥
You have **fixed the CORS**, but now you’re seeing this:

Uncaught Error: @vitejs/plugin-react can't detect preamble. Something is wrong.
at Home.jsx:9:5

✅ **This error means:**
Vite’s React plugin **(@vitejs/plugin-react)** needs the Vite "preamble" (`@vite/client`) to be loaded **before** your React app.
Preamble = Vite’s internal HMR client, fast refresh, etc.

But right now, **WordPress page is trying to load only your `main.jsx`** — **without** loading the Vite internal runtime (`@vite/client`).

That's why React plugin **crashes**.

# 🎯 How to fix:

You need to manually load **`@vite/client`** before your `main.jsx` when running in development mode (not needed for production).

✅ In your WordPress theme, **enqueue `@vite/client` first**, then your `main.jsx`.

### 📄 Correct WordPress enqueue setup for Vite:

In your `functions.php` (or wherever you enqueue scripts):

<?php

function theme_enqueue_scripts() {
    $is_dev = str_contains($_SERVER['HTTP_HOST'], '.ddev.site') && file_exists(get_theme_file_path('vite.config.js'));

    if ($is_dev) {
        // Dev mode: load Vite client and entry point separately

        // 1. Vite client (for HMR and React refresh)
        wp_enqueue_script('vite-client', 'https://wprock.ddev.site:5173/@vite/client', array(), null);

        // 2. Your main app entry
        wp_enqueue_script('theme-main', 'https://wprock.ddev.site:5173/src/main.jsx', array('vite-client'), null);
    } else {
        // Prod mode: load bundled files from /dist
        $manifest = json_decode(file_get_contents(get_theme_file_path('dist/manifest.json')), true);
        $main = $manifest['src/main.jsx']['file'];

        wp_enqueue_script('theme-main', get_template_directory_uri() . '/dist/' . $main, array(), null, true);
    }
}
add_action('wp_enqueue_scripts', 'theme_enqueue_scripts');
?>

# 🛠 Why?

| Script | Purpose |
|———————-|———————————————-|
| `@vite/client` | Internal Vite HMR runtime, hot refresh |
| `src/main.jsx` | Your React app entry point |

If you skip `@vite/client`, Vite can’t inject React Fast Refresh or HMR → React plugin crashes.

# 🧠 So what happened now:
– You **fixed** CORS and HMR config ✅
– You **missed** loading Vite preamble `@vite/client` ✅ (now fixing)
– Now **React + Vite + WordPress theme will work properly** ✅

# ⚡ Quick Checklist (Final):

– [x] Vite running with `npm run dev`
– [x] `@vite/client` loaded first
– [x] `src/main.jsx` loaded after
– [x] Browser loads WordPress normally without JS crash
– [x] HMR (Hot Module Reload) works inside WordPress 🎉

Would you like me to show you a quick **cleaner version** where all this is wrapped inside a PHP helper (`vite_asset()` function)?
(Lot of people using Vite with WordPress use this to avoid repeating code.) 🚀
Should I?

User
<script>

</script>
<script>

</script>

EOT;
} else {
// Production: load from built assets
$manifest_path = get_template_directory() . '/dist/.vite/manifest.json';
if (!file_exists($manifest_path)) return;

$manifest = json_decode(file_get_contents($manifest_path), true);
if (!isset($manifest[$entry])) return;

$main = $manifest[$entry];

// CSS
if (!empty($main['css'])) {
foreach ($main['css'] as $css_file) {
wp_enqueue_style(
'vite-style',
get_template_directory_uri() . '/dist/assets/' . basename($css_file),
[],
null
);
}
}

// JS with type="module"
add_filter('script_loader_tag', function ($tag, $handle) {
if ($handle === 'vite-js') {
$tag = str_replace('

Assistant
Excellent — you're very close again! 🔥
Your code is **90% correct** but **needs a few small important fixes** for a perfect WordPress + Vite + React theme integration.

Let’s go through it carefully:

# 🛠 Problems in your current code:

### 1. **Environment check**

$is_dev = wp_get_environment_type() === 'development';

✅ Good, but **`wp_get_environment_type()`** depends on how your WordPress is configured (`wp-config.php`).
If you didn't define it, WordPress treats it as "production" even on DDEV.

👉 Better: Detect based on domain (`.ddev.site`) + fallback.

### 2. **Wrong manifest path**

$manifest_path = get_template_directory() . '/dist/.vite/manifest.json';

⛔ **Wrong:**
Vite generates `dist/manifest.json` (not `.vite/manifest.json`).

✅ Should be:

$manifest_path = get_template_directory() . '/dist/manifest.json';

### 3. **Incorrect asset URL generation**

get_template_directory_uri() . '/dist/assets/' . basename($css_file)

⛔ **Wrong:**
The `basename()` removes folders! But Vite manifest gives **full relative paths** like:

{
  "src/main.jsx": {
    "file": "assets/main-xyz123.js",
    "css": ["assets/main-xyz123.css"]
  }
}

You should not `basename()`.
✅ Correct is:

get_template_directory_uri() . '/dist/' . $css_file

### 4. **Not scoped properly**
Instead of direct `echo`, better to **hook into `wp_head`** safely or echo at a specific action hook to avoid output ordering issues.

# ✨ Here’s your fully corrected version:

<?php
function wp_react_vite_assets() {
    $dev_server = 'https://wprock.ddev.site:5173';
    $entry = 'src/main.jsx';

    // Smarter dev check: DDEV detection
    $is_dev = (strpos($_SERVER['HTTP_HOST'], '.ddev.site') !== false);

    if ($is_dev) {
        // Dev mode: load Vite from dev server
        echo <<<EOT
<script type="module" crossorigin src="$dev_server/@vite/client"></script>
<script type="module" crossorigin src="$dev_server/$entry"></script>
EOT;
    } else {
        // Production mode: load compiled assets
        $manifest_path = get_template_directory() . '/dist/manifest.json';
        if (!file_exists($manifest_path)) return;

        $manifest = json_decode(file_get_contents($manifest_path), true);
        if (!isset($manifest[$entry])) return;

        $main = $manifest[$entry];

        // Load CSS if exists
        if (!empty($main['css'])) {
            foreach ($main['css'] as $css_file) {
                wp_enqueue_style(
                    'vite-style',
                    get_template_directory_uri() . '/dist/' . $css_file,
                    [],
                    null
                );
            }
        }

        // Force module type for JS
        add_filter('script_loader_tag', function ($tag, $handle) {
            if ($handle === 'vite-js') {
                $tag = str_replace('<script ', '<script type="module" ', $tag);
            }
            return $tag;
        }, 10, 2);

        wp_enqueue_script(
            'vite-js',
            get_template_directory_uri() . '/dist/' . $main['file'],
            [],
            null,
            true
        );
    }
}
add_action('wp_head', 'wp_react_vite_assets'); // load in <head> early

# ✅ Why these fixes are important:

| Problem | Correct fix |
|————————————|——————————————–|
| `.vite/manifest.json` wrong path | Use `dist/manifest.json` |
| `basename()` wrong asset URL | Use full path from manifest |
| Possible production environment mismatch | Detect `.ddev.site` |
| Output order | Hook into `wp_head` instead of direct echo randomly |

# 🚀 Now full flow:

| Environment | Action |
|——————-|——————————————-|
| Dev (`.ddev.site`) | Load from `https://wprock.ddev.site:5173` live |
| Prod | Load from `/dist/manifest.json` built files |

No CORS error ✅
No preamble missing error ✅
Perfect React/Vite HMR inside WordPress ✅

Would you also like me to give a **small helper `vite_asset()` function** so you can cleanly print assets in PHP like:

echo vite_asset('src/main.jsx');

?
(That’s how modern WordPress+Vite setups like Bedrock/Sage handle it — very clean.) 🚀
Should I prepare that too? 🔥

User
No all above code is correct just https://wprock.ddev.site/ not showing anything
Assistant
Got it — let's slow down and **focus on the real issue** you're describing:

You’re saying:
> "All above code is fine — but https://wprock.ddev.site/ **shows a blank page** (nothing displaying)."

✅ Your WordPress + React + Vite theme setup is basically correct.
✅ No CORS error now.
✅ No Vite preamble error now.

# 🎯 **What this actually means:**
If **https://wprock.ddev.site** is showing a **blank page**:
– WordPress **PHP is working** (no fatal error → otherwise you'd see 500 error).
– But your **React app is NOT rendering anything** into the page.


### 🧠 Likely Causes:

| Cause | Explanation |
|:—–|:————|
| React App is not properly mounting | Your `src/main.jsx` is probably NOT calling `ReactDOM.createRoot` properly into the correct `div` |
| Missing `#app` div | WordPress template may be missing a “ for React to render inside |
| Dev server assets loaded but nothing rendered | Good sign — just a missing DOM hook |

# 🔥 Checklist to fix blank screen

## 1. Make sure your WordPress `page.php`, `index.php`, `front-page.php` (wherever) has:

<div id="app"></div>

✅ This `div` must exist **before** React tries to mount.

Otherwise React has no DOM element to hook into.

## 2. Your `src/main.jsx` **must** look like:

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.jsx';

const root = document.getElementById('app'); // 👈 Must match div id="app"

if (root) {
  ReactDOM.createRoot(root).render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  );
} else {
  console.error('No #app div found!');
}

✅ This way, React **mounts** inside “ properly.

# 🧹 To summarize:

| What you should check | How |
|————————|—-|
| Theme template | Has “ |
| Vite dev server running? | `npm run dev` must be running |
| ReactDOM code | Using `document.getElementById('app')` |
| Browser console | No JS error like `Cannot read property 'createRoot' of null` |

# 📋 Quick Example:

**WordPress theme template (`page.php`)**:

<?php get_header(); ?>
<div id="app"></div>
<?php get_footer(); ?>

**React `main.jsx`**:

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.jsx';

const root = document.getElementById('app');
if (root) {
  ReactDOM.createRoot(root).render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  );
}

# 🚑 If still blank after this:
– Open browser console → Any errors?
– Check **Network Tab** → Is `main.jsx` loaded successfully?
– Check **Sources Tab** → Is React code injected properly?

# ⚡ Final note:

✅ **If “ is missing, React has nowhere to render → page stays empty.**

✅ **If Vite is not running, page stays empty or stuck.**

✅ **If ReactDOM createRoot points to wrong ID, blank screen.**

Would you like me to show you a **very clean minimal working WordPress + Vite + React example** repo structure? (like 5-minute setup) 🚀
If yes, I can write it for you!
It'll remove all guesswork.

Do you want it? 🎯

User
Uncaught Error: @vitejs/plugin-react can't detect preamble. Something is wrong.
at Home.jsx:9:5
Assistant
Okay, now I see very clearly —
Let's **pause and properly fix** what you are facing.

You again have this **same error**:

> `Uncaught Error: @vitejs/plugin-react can't detect preamble. Something is wrong.`

and it is happening **in `Home.jsx` at line 9**.

✅ This error **100%** means:
**Vite React Plugin (`@vitejs/plugin-react`) expects that `@vite/client` is loaded before your React app.**

But **your WordPress page** is **only loading your `main.jsx`**, and **NOT loading `@vite/client` first**.

# 🎯 So, the actual problem is simple:

– You **need** to load two things from Vite dev server:
– `@vite/client`
– your `src/main.jsx`
– And `@vite/client` **must be loaded first** before `main.jsx`.

# 🔥 Very important:

You are using this PHP right now:

echo <<<EOT
<script type="module" crossorigin src="$dev_server/@vite/client"></script>
<script type="module" crossorigin src="$dev_server/$entry"></script>
EOT;

✅ This looks **correct**.

But now the critical thing is:
– When you visit **https://wprock.ddev.site**,
**do you see `@vite/client` loaded** in browser network tab?

If `@vite/client` fails to load (for example: 404 error),
then Vite React Refresh breaks and you get **"can't detect preamble"** error.

# 🧠 Let's fix step-by-step:

## Step 1: Make sure `@vite/client` is accessible

In browser dev tools:
– Open **Network Tab**.
– Refresh the page.
– See if there is **a request for `https://wprock.ddev.site:5173/@vite/client`**.
– Is it **successful** (200) or **error** (404, failed)?

If `@vite/client` is not reachable, then React Fast Refresh crashes.

## Step 2: Fix vite.config.js if needed

In your `vite.config.js`, **make sure**:

server: {
  host: '0.0.0.0',
  port: 5173,
  strictPort: true,
  cors: true,
  origin: 'https://wprock.ddev.site',
  headers: {
    'Access-Control-Allow-Origin': '*',
  },
  hmr: {
    protocol: 'wss', // because wprock.ddev.site is https
    host: 'wprock.ddev.site',
    port: 5173,
  },
},

✅ Ensure `host: '0.0.0.0'`, not `localhost` inside Vite server.

## Step 3: Check browser console

When you refresh `https://wprock.ddev.site`,
– Are there any **404 errors**?
– Especially for `@vite/client`?

Example bad error:

GET https://wprock.ddev.site:5173/@vite/client net::ERR_CONNECTION_REFUSED

If you see this, it means **WordPress cannot reach your Vite dev server**.

# 📋 Full checklist:

| Check | How |
|:——|:—|
| `@vite/client` loaded first | Yes (Network tab) |
| No CORS error | Yes |
| Vite dev server running | `npm run dev` must be active |
| Correct host in vite.config.js | `0.0.0.0`, wprock.ddev.site |
| HTTPS or WSS consistent | Both must match (not HTTP HTTPS mixed) |

# 🛠 What you should do immediately:

1. Open Chrome DevTools → Network Tab → search for `client`
2. Check if `https://wprock.ddev.site:5173/@vite/client` is loaded ✅ or failed ❌
3. If failed → your Vite server is not exposing correctly
4. Fix `vite.config.js` server settings
5. Restart Vite server (`npm run dev` again)

# ⚡ Quick Tip:

In some DDEV environments, you might need to **port-forward 5173** if your Vite server inside Docker is isolated.

Example command:

ddev proxy --port 5173

Or set a custom proxy in `.ddev/config.yaml`.

# 🚑 In simple words:
**This is not a React problem.**
**This is Vite client injection problem.**

Once `@vite/client` loads properly → your error `can't detect preamble` will disappear.

Would you like me to give you a **simple way to test if Vite is properly reachable from WordPress** before even React loads? (Like a quick Vite ping test 🔥)
Should I? 🚀
It will make debugging much faster.

Leave a Comment

Your email address will not be published. Required fields are marked *