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_URLis 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>.jsin development<theme>.min.jsin 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:
| Chunk | Typical content |
|---|---|
framework | React, React DOM, scheduler |
router | React Router |
motion | GSAP and its plugins |
vendors | remaining node_modules dependencies |
| theme entry | application 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 KBraw per JavaScript asset170 KBgzip 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
- Run
npm run prodfromconfigs/. - Resolve build errors and review bundle warnings.
- Deploy the generated
dist/tree, including manifests and compressed siblings. - Deploy
assets/render/, including static fragments when SSG is enabled. - Start and health-check
serve.mjsonly when the project uses SSR. - Confirm the server returns the correct MIME type and content encoding.
- Purge any host or CDN HTML cache.
- Invalidate the ReactWP cache generation after the release is live.
- 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.