{} CodeLift

Laravel + React Starter Kit: Docker-verified fork

We took the official laravel/react-starter-kit (Inertia + React 19 + shadcn/ui + Fortify), ran it inside Docker Desktop, and published a production-hardened fork. The test suite goes from the upstream 40 passed to 44 passed / 151 assertion…

Pub 2026-04-19 Verified 2026-04-19 Upd 2026-04-19

Verification environment

  • PHP 8.5.5
  • Laravel 13.5.0
  • Composer 2.9.7
  • Node 22.22.2
  • npm 10.9.7
  • Database SQLite (tests)
  • OS Docker Desktop (php:8.5-cli-bookworm)

Laravel + React Starter Kit: Docker-verified fork

We took the official laravel/react-starter-kit (Inertia + React 19 + shadcn/ui + Fortify), ran it inside Docker Desktop, and published a production-hardened fork. The test suite goes from the upstream 40 passed to 44 passed / 151 assertions. This article covers the React / Inertia-specific findings only.

The backend improvements (.env.example, timezone, security headers, HTTPS forcing, rate limiting, auth logging) are identical across the React, Vue, and Livewire forks, so they're consolidated in the pillar article:

Hardening the shared Laravel starter-kit backend

This article is the cluster piece: it covers only what's specific to the React Starter Kit.

Target

Item Value
Name Laravel + React Starter Kit
Official URL https://github.com/laravel/react-starter-kit
Improved fork https://github.com/codelift-dev/react-starter-kit/tree/improvements
Stack Laravel 13 + Inertia + React 19 + TypeScript + shadcn/ui + Fortify
Upstream / improved license MIT / MIT
Verification date 2026-04-19
Upstream commit laravel/react-starter-kit@2e22614

Environment

Same as the pillar article (Docker Desktop, PHP 8.5.5 / Laravel 13.x / Node 22.22.2). Reproduce via the fork's codelift/ files:

git clone https://github.com/codelift-dev/react-starter-kit.git
cd react-starter-kit
git checkout improvements
docker compose -f codelift/docker-compose.yml build
docker compose -f codelift/docker-compose.yml run --rm app

React-specific findings

A. npm run build before composer install fails opaquely

Specific to the React / Vue kits (the Livewire kit doesn't have this).

Symptom: on a fresh clone, running npm run build first fails the Vite build with:

require(.../vendor/autoload.php): Failed to open stream: No such file or directory
in artisan on line 10

Cause: the @laravel/vite-plugin-wayfinder plugin in vite.config.ts shells out to php artisan wayfinder:generate during the build. Wayfinder generates typed TypeScript route helpers from Laravel's route definitions, which means booting artisan at build time. Without Composer dependencies, artisan can't load vendor/autoload.php and dies.

Why it's nasty: the error never mentions composer install. A first-time contributor suspects a Vite or Wayfinder bug and burns time in the wrong direction. Only someone fluent in Laravel reads vendor/autoload.php and immediately thinks "Composer hasn't run yet."

Fix: a README Setup section spelling out the composer installnpm run build order, plus a note that Wayfinder calls artisan at build time so the reason for the ordering is clear.

React 19 + Inertia initial state and CSP

The React Starter Kit uses React 19. Inertia bridges server-side routing and React components: each navigation returns a JSON "page object" the server produces and React renders.

The catch is the first load. Inertia must embed the page component and initial props into the initial HTML. That goes into a root-element attribute — <div id="app" data-page="{...encoded props...}"> — emitted by the @inertia Blade directive.

This "embed initial props directly in HTML" design is the obstacle when tightening Content-Security-Policy. To drop 'unsafe-inline' entirely and move to a nonce-based policy, you'd have to reach into Inertia's response rendering (the HandleInertiaRequests middleware or Inertia's response class) and thread a nonce through.

CodeLift's fork introduces a CSP (pillar finding D) but keeps 'unsafe-inline' in script-src as a conservative setting. The nonce migration involves modifying Inertia and is a separate topic for a follow-up.

For contrast, the Livewire kit — which doesn't use Inertia — does not embed initial state in an inline script, so it can move to a nonce-based CSP without 'unsafe-inline'. That technique is detailed in the Livewire CSP nonce article.

J. Settings endpoints — only password update is throttled

The React Starter Kit's routes/settings.php attaches an explicit throttle only to the password update route.

Route::patch('settings/profile', ...)->name('profile.update');         // no throttle
Route::delete('settings/profile', ...)->name('profile.destroy');       // no throttle
Route::put('settings/password', ...)->middleware('throttle:6,1')...;   // throttled

On a compromised session, repeated profile edits or account-deletion attempts run under the global default.

Fix: in the React/Vue kits, settings mutations are plain PATCH / DELETE HTTP verbs, so adding throttle middleware at the route definition is enough.

-    Route::patch('settings/profile', ...)->name('profile.update');
+    Route::patch('settings/profile', ...)->middleware('throttle:10,1')->name('profile.update');
-    Route::delete('settings/profile', ...)->name('profile.destroy');
+    Route::delete('settings/profile', ...)->middleware('throttle:3,1')->name('profile.destroy');

Account deletion is terminal, so it's tightened to 3/minute. (The Livewire kit can't reuse this fix as-is because it routes through Route::livewire component AJAX — see the Livewire article.)

Commit layout of the improved fork

8 commits on the improvements branch:

Commit Finding Source
README Setup A (React/Vue-specific) this article
.env.example comments B pillar
timezone via env C pillar
URL::forceScheme E pillar
SetSecurityHeaders middleware + test D pillar
layered login rate limiter G pillar
auth log channel + subscriber + test I pillar
settings throttle J (this article) this article

Tests: upstream 40 passed → improved 44 passed / 151 assertions (SecurityHeaders +2, AuthLogging +2, all existing still green).

Before / after (React-specific part)

Dimension Official Improved
php artisan test 40 passed / 136 assertions 44 passed / 151 assertions
Build-order documentation None (Wayfinder footgun) README Setup section
Settings endpoint throttle Password only + profile update / destroy

The before/after for the 6 shared backend findings is in the pillar article.

When this fits

  • You're starting a production product on laravel/react-starter-kit and want the floor set first.
  • You're choosing between React / Vue / Livewire and want the React production cost. → The verdict is in the three-way comparison (backend cost is identical across all three).

Reproduce and adopt

git clone https://github.com/codelift-dev/react-starter-kit.git
cd react-starter-kit
git diff main improvements -- . ':!codelift'

Each commit is standalone, cherry-pickable.

Related

License

Upstream and improved fork both MIT. Findings reflect the verification date.

Featured in comparisons

Related articles