cretueusebiu/laravel-nuxt
Integrates Nuxt.js with Laravel for building server-rendered JavaScript apps backed by a Laravel API. It provides scaffolding, authentication support, and a smooth workflow for combining Laravel’s backend with Nuxt’s frontend.
cretueusebiu/laravel-nuxt is the glue layer between a Laravel app and a Nuxt (Vue 2) frontend: it provides a catch-all controller that serves/proxies the Nuxt-rendered HTML, artisan commands to scaffold and build the Nuxt app, and a small config file for pointing Laravel at the running Node server.
3.0.0 is primarily a compatibility release (Laravel 8 / dependency refresh) plus a validation-error fix. If you are on Laravel 6 or 7, do not upgrade — pin ^2.0. If you are on Laravel 8+, this is the version to be on.
Minimal first steps:
composer require cretueusebiu/laravel-nuxt (service provider is auto-discovered on Laravel 5.5+; no config/app.php edit needed).php artisan vendor:publish --provider="LaravelNuxt\NuxtServiceProvider" — publishes config/nuxt.php (server URL / headers / middleware) plus the Nuxt scaffold (nuxt.config.js, package.json scripts, and a resources/js/{pages,layouts,components,store,plugins,middleware} tree) and resources/views/app.blade.php.php artisan nuxt:install if the scaffold was not created by publishing; then npm install.routes/web.php:
Route::get('{path?}', 'LaravelNuxt\NuxtController@index')->where('path', '.*');
php artisan serve and the Nuxt dev server (npm run dev / the package's artisan wrapper — check php artisan list nuxt for the exact command names available in your version). Point NUXT_URL (default http://localhost:3000) at it.Where to look first when something is wrong, in order: config/nuxt.php (is Laravel talking to the right Node URL?), nuxt.config.js (srcDir, buildDir, axios baseURL), routes/web.php (route ordering), then storage/logs/laravel.log (a dead Node server surfaces as a Guzzle/connection error, not a Nuxt error).
First real use case: a page that posts a form to a Laravel API endpoint. In 3.0.0 the validation path now behaves the way you expect — a failed FormRequest returns a 422 with a JSON errors bag that this.$axios can catch client-side, instead of being swallowed by the SSR render path.
Two-process topology. Nuxt runs as a Node server; Laravel proxies to it for SSR, and Nuxt's axios instance calls back into Laravel for /api/*. Keep the boundaries clean: Nuxt owns rendering, Laravel owns data and auth. Don't have Nuxt hit the database and don't have Blade render data the Nuxt page needs.
Environment wiring. Keep NUXT_URL per environment and treat it as a hard dependency. A common pattern is a Nuxt-side axios.baseURL that is http://localhost:8000 (or a proxy) in dev and the app's own origin in production, so browser calls hit Laravel directly and Laravel serves HTML from Nuxt.
CSRF / session auth. Browser → Laravel requests need the standard Sanctum/CSRF dance: credentials: true on axios, read the XSRF-TOKEN cookie, echo it as X-XSRF-TOKEN. Server-side, the SSR proxy needs the user's cookies forwarded (via the headers/middleware options in config/nuxt.php) or SSR will render as a guest and hydrate into an authenticated page — the classic auth flash.
Auth-gated pages. Pattern: render the shell server-side, then fetch the authenticated payload client-side in fetch()/asyncData with the cookie attached. Reserve SSR data fetching for genuinely public pages; it avoids forwarding session state through the proxy.
Route layout. API routes registered by RouteServiceProvider::mapApiRoutes() come before web routes, so /api/* wins over the catch-all. Keep the catch-all as the final statement of routes/web.php and exclude /storage and any webhook paths above it.
Deployment. Two options: (a) npm run build + a supervised nuxt start (pm2/systemd/Docker sidecar) with NUXT_URL pointing at it, or (b) php artisan nuxt:generate for a static SPA served from public/Blade, which removes Node from production entirely. Option (b) is the lower-risk default for most apps; choose (a) only when you actually need per-request SSR.
Testing. Feature tests shouldn't need a live Node process. Either restrict tests to /api routes, or Http::fake() the Nuxt render call so GET / returns a stub HTML body. Put npm run build (not start) in CI to catch template breakage.
Extension points. Override the published config/nuxt.php (URL, forwarded headers, middleware stack applied before rendering), swap the catch-all for your own controller that wraps LaravelNuxt\NuxtController, and use the middleware list to inject tenant/locale resolution before the render.
RouteServiceProvider namespace change), not separately.Accept: application/json hacks, a custom Handler::invalid() override, or expectsJson() middleware workarounds purely to get 422s out of the SSR path, delete them and re-test. Conversely, if any flow depended on a redirect-back-with-$errors after a failed form post, verify it still redirects — the fix routes these into proper JSON error responses.'LaravelNuxt\NuxtController@index' still resolves), and if you prefer the tuple form prefer [NuxtController::class, 'index']. String actions like '@NuxtController' from old tutorials break under the namespace-less default.where('path', '.*'), every unmatched URL renders the SPA shell with a 200. Implement the not-found page in Nuxt and return a real 404 status from the controller if SEO matters.{path?} combined with .* can conflict with your / route or with route:cache in odd ways; if the root page 404s, drop the ? and register an explicit / route above the catch-all.config/nuxt.php and add a health check; consider a cached/static fallback.Cookie (and Authorization if you use tokens) in the render headers.vendor:publish --force overwrites nuxt.config.js and the blade view; diff before forcing, and commit the published files so upgrades are reviewable.composer update --with-dependencies on a branch and check for conflicts with pinned Guzzle/Symfony versions in your app before merging.NUXT_URL and curl the Node server. Blank page but 200 → check buildDir/srcDir and whether npm run build actually ran. 419 on POST → CSRF token header. 422 with no errors key → you re-added a custom exception handler on top of the new behavior.How can I help you explore Laravel packages today?