Skip to main content

Deployment and Performance

Production Build

Run the full production pipeline from configs/:

npm run prod

This compiles themes, plugins, and mu-plugins. For each configured theme it also:

  • minifies JavaScript and the main stylesheet
  • splits the initial JavaScript into stable framework, router, motion, vendor, and application assets
  • creates lazy chunks for dynamically imported templates
  • extracts SCSS imported by JavaScript into CSS chunks
  • optimizes copied JPG, PNG, GIF, WebP, and SVG theme media through Sharp and SVGO
  • removes stale chunks and the development entry file
  • emits Brotli and gzip variants for theme JavaScript and CSS
  • writes and prints the production bundle report
  • builds the universal renderer and hybrid rendering manifests
  • generates configured static routes when RWP_SITE_URL is set

npm run build is a development build. It keeps readable JavaScript, source maps, and stable chunk names. npm run watch does the same work continuously.

Node is not required to serve client or static routes. Keep assets/render/ in the deployment even when SSR is disabled: it contains the template manifests and any generated static fragments. A permanent Node process is only required for server routes and runtime static regeneration.

Entrypoint Manifest

Theme JavaScript is not hardcoded to one filename in PHP. Webpack writes:

dist/wp-content/themes/<theme>/assets/js/entrypoints.json

The manifest lists the scripts and extracted styles in dependency order. ReactWP reads it during wp_enqueue_scripts, enqueues each file, and marks the JavaScript as defer.

This is why the same PHP runtime can load:

  • <theme>.js in development
  • <theme>.min.js in production
  • all required shared chunks without a manual filename change

Do not hand-edit entrypoints.json. It is generated output and must be deployed with the assets it references.

Code Splitting

The starter separates large, reusable dependencies from project code:

ChunkTypical content
frameworkReact, React DOM, scheduler
routerReact Router
motionGSAP and its plugins
vendorsremaining node_modules dependencies
theme entryapplication bootstrap and shared project code

Templates registered with () => import(...) become lazy route chunks. Keep route-specific libraries inside the template that needs them so they do not migrate into the initial entry.

Bundle splitting improves caching and avoids one oversized file, but it does not make unused libraries free. Audit imports and only initialize expensive features on routes that use them.

CSS Delivery

The permanent theme stylesheet is always emitted at:

assets/css/<theme>.min.css

SCSS imported from a template JavaScript file is extracted into an additional CSS chunk. The chunk is downloaded with the matching lazy template instead of being injected through JavaScript.

Keep above-the-fold shell styles in the main stylesheet or the small critical CSS block printed by the theme. Keep truly route-specific styles beside their template.

Bundle Report and Budgets

Run the report independently with:

npm run report:themes

It reports raw, gzip, and Brotli sizes for every initial script. It also detects a production bundle that accidentally contains React's development JSX runtime.

Default warning budgets are:

  • 250 KB raw per JavaScript asset
  • 170 KB gzip for the complete initial JavaScript entry

Override them for one command with:

$env:RWP_MAX_JS_ASSET_KB = '220'
$env:RWP_MAX_INITIAL_GZIP_KB = '150'
$env:RWP_BUNDLE_BUDGET_STRICT = '1'
npm run prod

With strict mode enabled, budget warnings fail the command. A development JSX runtime in production always fails the report.

Brotli and Gzip

Production creates .br and .gz siblings for theme JavaScript and CSS. The root .htaccess prefers Brotli, falls back to gzip, and adds Vary: Accept-Encoding when Apache serves those files.

Deploy all three versions:

app.min.js
app.min.js.br
app.min.js.gz

The browser still requests app.min.js; the web server selects the encoded representation. Do not enqueue the .br or .gz URL directly.

The included rules target Apache. Nginx, a managed host, or a CDN may require equivalent configuration in that platform. Confirm the response with browser DevTools: the original URL should return Content-Encoding: br or gzip.

Cache Headers

The included Apache configuration sends long-lived immutable headers for JavaScript, CSS, and fonts, and a shorter public lifetime for images. Production chunks are content-hashed, while initial assets receive a version query generated by PHP.

After publishing changed files, use ReactWP > Cache in the WordPress admin to advance the ReactWP cache generation and invalidate static plus public/private SSR fragments. See Cache and Invalidation.

Deployment Checklist

  1. Run npm run prod from configs/.
  2. Resolve build errors and review bundle warnings.
  3. Deploy the generated dist/ tree, including manifests and compressed siblings.
  4. Deploy assets/render/, including static fragments when SSG is enabled.
  5. Start and health-check serve.mjs only when the project uses SSR.
  6. Confirm the server returns the correct MIME type and content encoding.
  7. Purge any host or CDN HTML cache.
  8. Invalidate the ReactWP cache generation after the release is live.
  9. Test a direct load, hydration, an internal route transition, back/forward navigation, and private output if the project has one.

LCP Guidance

Use the critical media pipeline only for assets required by the first visible view. The LCP image should have a stable layout slot and should not wait behind a route-only template import or a deferred media group.

Do not classify every image as critical. That competes with the actual LCP resource and turns preloading into extra network pressure.

Static and server routes can paint their HTML and extracted template CSS before the application bundle hydrates. Keep their render output deterministic and avoid making the LCP element depend on a browser-only effect. See Hybrid Rendering.

Before deploying SSR or automated SSG, review Configuration Reference for environment variables, security defaults, timeout units, and cache-scope defaults.