Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Ahondev/portfolio-v2/llms.txt

Use this file to discover all available pages before exploring further.

WP SSR Framework uses Vite as the build tool and asset pipeline for the React SPA. The PHP side, through the Vite class, automatically detects whether you are in development (using the Vite dev server with hot module replacement) or production (reading hashed filenames from a build manifest). You never need to hard-code asset URLs.

How PHP Serves Vite Assets

The Vite singleton class (src/SSR/Vite.php) resolves the correct asset URLs for each environment.

Development Mode

When Vite’s dev server is running, it writes the server URL to a public/hot file. The PHP Vite class reads this file to detect development mode:
src/SSR/Vite.php
public function devServerRunning(): ?string
{
    $hot = client_root('/public/hot');
    if (!file_exists($hot)) {
        return null;
    }
    return trim(file_get_contents($hot));
}
In dev mode, Vite::instance()->assets() returns:
[
    'dev'    => true,
    'url'    => 'http://localhost:8080',
    'client' => 'http://localhost:8080/@vite/client',  // Vite HMR client
    'entry'  => 'http://localhost:8080/src/main.tsx',   // app entry point
    'css'    => [],
]

Production Mode

In production, public/hot does not exist. Instead, Vite reads the Vite build manifest at dist/.vite/manifest.json and caches it as a WordPress transient for 24 hours:
src/SSR/Vite.php
public function manifest()
{
    $manifestPath = client_root('/dist/.vite/manifest.json');
    if (!file_exists($manifestPath)) {
        return null;
    }
    $key = 'vite_manifest_' . filemtime($manifestPath);
    return $this->remember($key, DAY_IN_SECONDS, function () use ($manifestPath) {
        return json_decode(file_get_contents($manifestPath), true);
    });
}
In production mode, Vite::instance()->assets() reads the index.html entry from the manifest and returns:
[
    'dev' => false,
    'js'  => ['/client/dist/assets/index-AbCdEfGh.js'],
    'css' => ['/client/dist/assets/index-XyZ12345.css'],
]
The js and css paths are prefixed with /client/dist/ and taken directly from the manifest’s index.html entry. If the manifest is missing or has no index.html key, both arrays are empty. The Vite class is used by WebController::view() to inject the correct <script> and <link> tags into every HTML response.

vite.config.ts

The Vite configuration in web/client/vite.config.ts:
vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import tailwindcss from '@tailwindcss/vite';
import path from "path";
import fs from "fs";

export default defineConfig(({ mode }) => ({
  server: {
    host: "::",
    port: 8080,
    cors: true,
    hmr: { overlay: false },
  },
  plugins: [
    react(),
    mode === "development" && hotFilePlugin(),  // writes public/hot on dev server start
    tailwindcss(),
  ].filter(Boolean),
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),  // @/ alias → src/
    },
  },
  build: {
    manifest: true,           // generates dist/.vite/manifest.json
    outDir: "dist",
    assetsDir: "assets",
    emptyOutDir: true,
  },
}));
Key configuration points:
  • @/ alias — maps to web/client/src/. Use @/components/Button instead of relative paths.
  • manifest: true — required for PHP to read hashed filenames in production.
  • Port 8080 — the Vite dev server runs on port 8080; the WordPress dev server on port 8888.
  • hotFilePlugin — a custom Vite plugin that writes public/hot when the dev server starts and removes it on exit. This is the signal PHP uses to switch to dev mode.

The hotFilePlugin

The hotFilePlugin is defined at the bottom of vite.config.ts. On dev server start it:
  1. Deletes any existing public/hot file (cleanup from a previous crash)
  2. Writes http://localhost:8080 to public/hot
  3. Registers process.on('exit') and signal handlers (SIGINT, SIGTERM, SIGHUP) to delete the file on shutdown
vite.config.ts
function hotFilePlugin() {
  return {
    name: "hot-file-plugin",
    configureServer(server) {
      const hotPath = path.resolve(__dirname, "public/hot");
      const devServerUrl = "http://localhost:8080";

      // Clean up any stale file from a previous crash
      try {
        if (fs.existsSync(hotPath)) fs.unlinkSync(hotPath);
      } catch {}

      fs.writeFileSync(hotPath, devServerUrl);

      const cleanup = () => {
        try {
          if (fs.existsSync(hotPath)) fs.unlinkSync(hotPath);
        } catch {}
      };

      process.on("exit", cleanup);
      ["SIGINT", "SIGTERM", "SIGHUP"].forEach((signal) => {
        process.on(signal, () => { cleanup(); process.exit(); });
      });
    },
  };
}

Development Workflow

1

Start the WordPress server

From the Bedrock project root:
php -S localhost:8888 -t web
2

Start the Vite dev server

In a separate terminal:
cd web/client
yarn dev
This starts Vite on http://localhost:8080 and writes web/client/public/hot.
3

Open the site

Navigate to http://localhost:8888. PHP detects public/hot, loads assets from the Vite dev server, and HMR is active — React components update instantly on save without a full page reload.
The hotFilePlugin automatically deletes public/hot when you stop the Vite dev server (Ctrl+C). If the file is left behind after a crash, PHP will attempt to load assets from the dead dev server. Delete web/client/public/hot manually in that case.

Production Build

cd web/client
yarn build
This compiles the application and writes output to web/client/dist/:
web/client/dist/
├── .vite/
│   └── manifest.json       ← read by PHP Vite class
├── assets/
│   ├── index-AbCdEfGh.js   ← hashed main bundle
│   ├── index-XyZ12345.css  ← hashed CSS bundle
│   └── ...                 ← code-split page chunks
├── index.html
└── robots.txt
The manifest maps source file paths to their hashed output names. PHP looks up the index.html entry to find the main JS and CSS files:
dist/.vite/manifest.json
{
  "index.html": {
    "file": "assets/index-AbCdEfGh.js",
    "css": ["assets/index-XyZ12345.css"],
    "isEntry": true
  },
  "src/pages/BlogPost.tsx": {
    "file": "assets/BlogPost-Mn0pQrSt.js"
  }
}
Run yarn build:dev (using vite build --mode development) to produce a non-minified build with source maps — useful for debugging production issues without running the dev server.

HTML Shell (src/SSR/client.php)

The PHP template that renders the HTML document for every page request. It:
  1. Sets window.__wp_data__, window.__wp_view__, and window.__wp_seo__ as an inline script
  2. Injects the Vite CSS <link> tags
  3. Renders <div id="root"></div> — the React mount point
  4. Injects the Vite JS <script> tags (either from dev server or production manifest)
This file is the only PHP template in the system. All other rendering is handled by React.
Never edit src/SSR/client.php to add page-specific content. All dynamic content should come through the $__wp_data__ payload. The client template is framework infrastructure.

Build docs developers (and LLMs) love