Laravel Model::shouldBeStrict(): make silent Eloquent bugs loud
Hidden gem: one line turns dropped fields, typo'd attributes and N+1 queries into loud errors in development. Tested on Laravel 13.38.
Clear, up-to-date guides for the stack you love: Laravel 13 on the back end, Vue 3 on the front end, and everything in between. Written by developers, for developers.
// Laravel 13 + Inertia 3
Route::get('/posts', fn () => Inertia::render('Posts', [
'posts' => Post::latest()->paginate(),
]));
<!-- resources/js/pages/Posts.vue -->
<script setup lang="ts">
defineProps<{ posts: Paginated<Post> }>()
</script>
<template>
<PostCard v-for="post in posts.data" :post="post" />
</template>Starter prompt
Laravel 13 is the back end. Vue 3 is the front end. Tick login, social login, Passport, and the screens you need.
Already have an app? Paste the error or the lockfiles.
Catch the bad line in a prompt.
Cross out the bad line in yours.
For your AI agent
Copy a prompt into the AI agent you already have open. Read the article when you want the reason.
For your AI agent
Copies a prompt. Paste it into the AI agent you already have open.
Read the articleFor your AI agent
Copies a prompt. Paste it into the AI agent you already have open.
Read the articleFor your AI agent
Copies a prompt. Paste it into the AI agent you already have open.
Read the articleFor your AI agent
Copies a prompt. Paste it into the AI agent you already have open.
Read the articleFor your AI agent
Copies a prompt. Paste it into the AI agent you already have open.
Read the articleFor your AI agent
Copies a prompt. Paste it into the AI agent you already have open.
Read the articleFor your AI agent
Copies a prompt. Paste it into the AI agent you already have open.
Read the articleLatest
Hidden gem: one line turns dropped fields, typo'd attributes and N+1 queries into loud errors in development. Tested on Laravel 13.38.
PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 fix two critical unauthenticated remote code execution bugs (CVSS 9.2). What happened, who is exposed, and how to patch Laravel servers on Docker, Forge, Vapor, Cloud and Coolify.
Hidden gem: onWatcherCleanup() aborts the previous keystroke's request and debounce timer before a watcher re-runs, and on unmount. With watch pause()/resume(). Tested on Vue 3.5.44.
Laravel 13.38 adds PreventStaleDatabaseReads, Connection::usingWritePdo(), CountCrashesAsExceptions for listeners/mail/notifications and a route-cache-safe /up.
Hidden gem: the Context facade stamps a request id, user and route on every log line and carries them into queued jobs. Hidden values stay out of logs. Tested on Laravel 13.37.
Laravel AI SDK 1.3 types structured responses for PHPStan, caps remote files at 100 MB and hardens file IDs. Two changes can break your app on upgrade.
Hidden gem: one line in AppServiceProvider turns every forgotten with() into one batched query. 51 queries became 2 in our Laravel 13.37 test.
AI SDK 1.2.2/1.2.3 add failure events and wider classification, Wayfinder 0.1.23 builds without a database, and Laravel 12.70 backports fixes.
Laravel 13.37 adds Rule::numeric(strict: true), queue:failed --queue, multi-id queue:forget, JPEG XL in the Image API, and withWhereHas column selection with a callback.
Wayfinder 0.1.22 and Precognition 2.1.1 add first-class QUERY support, so Route::query() finally works end-to-end with typed clients and live validation.
Hidden gem: Cache::flexible serves slightly stale data instantly and refreshes after the response. No user waits on a cache miss. Tested on Laravel 13.35.
Laravel 13.36 adds Rule:: builders for almost every validation rule, DB::olap() for PlanetScale, and a flag that stops queue workers sticking to the write database.
Laravel AI SDK 1.2.1 adds Stop::when, Stop::unless and StepResult::stop() to end agent runs early. Tested stop button, token budgets and a Vue 3 + Inertia 3 UI.
Hidden gem: Laravel's defer() runs CRM syncs, webhooks and analytics after the user gets their page. No queue worker, one line. Tested on Laravel 13.35.
Laravel AI SDK 1.1 and 1.2 add Agent Skills, approval for built-in tools, Cohere chat, MoveFile and OpenAI image classification. Tested code, a Vue approval prompt and upgrade notes.
The small releases you might have missed: Inertia 2.3.29 security backports, Laravel 12.69.3 queue and S3 fixes, Inertia Laravel 3.5.1 and Boost 2.10.1 to 2.10.3.
One hundred short Laravel and Vue guides for the searches under an Inertia app: forms, Fortify, sessions, queues, and the AI SDK.
DraftReply::fake() returns a canned string. assertPrompted checks the note. The test does not call the provider.
pendingApprovals means the agent stopped. Resuming that approval needs the conversation. A one-shot prompt cannot resume it.
RemembersConversations stores the thread. Check that the id belongs to the user before you continue it.
stream() can be returned from its own route as server-sent events. It is not an Inertia form response.
HasStructuredOutput defines a schema. The Vue page receives the fields you read from the response, not the provider payload.
Boost's inertia-vue-development skill matches the Inertia major you installed. It is not a Vue component.
boost:update rewrites the agent guidelines for the versions you installed. Boost is not an npm package.
Http::fake stubs the outbound request. The Inertia test asserts the prop. It does not call the real host.
Sail on WSL2 needs the HMR host set to localhost. Do not remove the kit's Vite plugins to add that block.
Only VITE_ keys reach import.meta.env. Vite 4+ restarts the dev server itself when .env changes. Secrets stay unprefixed.
Log the user id and the failure. Do not log the request body, the token, or the password.
.env.example lists the keys with empty secrets. .env stays off the git remote. Vite only sees VITE_ keys.
After config:cache, read config('app.name'). env() outside config files returns null.
php artisan migrate --force belongs in the release step. A Vue button must not be the thing that migrates production.
Laravel registers /up. A load balancer can hit it. The Vue app should not use it as a homepage.
Telescope is a dev dependency. The default gate should not let the public open /telescope.
File sessions break when two servers handle the next request. database sessions follow the user.
npm run build does not refresh the SSR bundle. Production needs build:ssr and a process that stays up.
Behind one load balancer, trustProxies(at: '*') makes Laravel see HTTPS. Do not do that on a machine exposed directly.
vendor/bin/pint --dirty formats the PHP you touched. It does not format Vue.
Laravel 11 and newer register routes, middleware, and the health URL in bootstrap/app.php. Do not add Http/Kernel.php back.
ShouldBroadcast queues the event. ShouldBroadcastNow runs it inside the request. The Vue listener is the Reverb guide.
Add broadcast to via() only after the Reverb page is in place. Do not open a second Echo client.
via() can return mail and database. The Vue bell reads the database row. It does not read the mailable.
MassPrunable deletes rows that match prunable(). Schedule model:prune. Do not point that scope at users.
schedule:list shows the next time. It does not store a last run. Write a timestamp from the command if the Vue page needs one.
queue:failed-table stores the failure. An admin page may show the name and the time. It does not show the payload.
queue:work is the long-running worker. queue:listen reloads your code each job and is the local command.
chunkById walks a table in a job. Do not chunk() an update, and do not do this work inside the HTTP request.
max:2048 on a file is 2 MB. PHP can reject the upload first, and Laravel then looks like the field was empty.
temporaryUrl gives Vue a short link to a private object. Do not make the bucket public to fix an img tag.
storage:link maps public/storage to storage/app/public. Laravel Cloud does not keep that symlink.
streamDownload writes the CSV. The button is an anchor, not an Inertia Link. cursor() keeps the memory flat.
temporarySignedRoute belongs in the mail. The Vue page opens after the signed middleware passes.
temporarySignedRoute plus the signed middleware. An Inertia Link cannot download the file. Use an anchor.
Store integer cents. Format with Number::currency on the server and pass a string. Do not add money in JavaScript.
Store UTC. Format with the user's timezone when you send the prop. Do not save local time into a timestamp column.
Set the locale from an allow-list. A query string is not a locale until you have checked it.
Feature::for($user)->active() becomes a boolean prop. The controller checks the same flag before it writes.
Log the subject and the user from the controller. Pass the latest rows as a prop. Do not log a password.
Cache::remember stores a public count. Forget that key when a post is created. Do not cache one user's drafts under a global key.
DB::transaction commits the post and its relations together. Redirect after the closure returns.
upsert writes a batch by a unique key. It skips model events, so a normal Vue form should still use create or update.
simplePaginate omits the total and last_page. The Vue footer uses previous and next only.
A resource collection of a paginator already includes links and meta. Do not paginate those rows again in Vue.
The restore route uses withTrashed() or the binding 404s before your controller runs.
SoftDeletes hides deleted rows from a normal query. Do not filter them again in Vue.
getRouteKeyName can return slug. The column has to be unique. The title is not a key.
Type-hint Post $post and the URL {post} resolves the row. A missing row is a 404.
with('author') loads the relation once. Reading it inside a loop is one query per row.
cursorPaginate needs a unique order. id is unique. A duplicated published_at skips and repeats rows.
when() adds a like query only when the search box is not empty. Bind the word. Do not paste it into SQL.
A missing post already 404s through route model binding. Do not build a second Inertia error page for it.
Send updated_at with the form and compare it before you write. Do not use HTTP 409 for that.
Inertia uses 409 when the asset version changed. The client reloads. It is not a validation error.
The kit's toast reads flash.toast. An error uses the same array with type error. with() does not reach it.
viewAny takes the model class, not a row. Call it on the index before you paginate.
role and permission middleware refuse the request. The Vue page only receives a boolean it can show.
Share the gate as a boolean. The controller still calls authorize on the POST.
authorize() in the controller is the check. The Vue button can hide itself later.
data() becomes ref. Methods become functions. An Inertia page still receives props. It does not gain Vue Router.
withDefaults fills a prop that is missing. A prop that arrives as null stays null.
A persistent layout does not receive the page props as its own props. usePage() does.
back() returns to the page that posted. to_route() is the right call when the next page is a specific list.
prefetch on a Link loads a GET page before the click. Do not prefetch a POST, a logout, or a page the user cannot open.
HandleInertiaRequests::share() runs for every Inertia response. Keep the query cheap and leave secrets out.
Email a signed URL and store only a hash of the token. Do not email a temporary password.
Update the signed-in user from the kit's profile page. Ignore their id on the unique email rule.
routes/api.php is already under /api. Add v1 inside the file and keep Inertia pages out of it.
Socialite starts with a top-level redirect. A Vue popup loses the session cookie on the way back.
A separate Vue origin needs supports_credentials and an explicit origin. A same-origin Inertia app needs neither.
createToken can take an ability list. The ability middleware belongs on the API route, not on an Inertia page.
A first-party Vue SPA uses the session cookie. createToken is for a mobile client or another product.
auth on the Laravel route is what sends a guest to login. A v-if on the Vue page does not.
Fortify's login limiter is five attempts a minute. The Vue page shows the message on the username field.
Fortify's username config key is the input name. The Vue login form has to post that same name.
password.confirm sends the user to Fortify's confirm screen. The Vue form posts the current password once.
Fortify already exposes the forgot-password and reset-password URLs. The Vue form posts those fields and no others.
The Vue starter kit already verifies email. The user model must implement MustVerifyEmail, and the resend form posts to Fortify.
Leave SESSION_DOMAIN empty on one host. Set the parent domain only when Vue and Laravel are real subdomains.
A 419 after deploy is a CSRF cookie that the browser did not send. The app key, the domain, and HTTPS are the usual causes.
Pass query on the generated Wayfinder function. Do not glue a question mark onto the URL by hand.
The Vue starter kit already has InputError. Pass it the validation string. Do not install a second kit.
Send enum cases as value and label props. Validate the posted string with Rule::enum.
useRemember keeps a filter in the history entry. It is the wrong place for a password or a draft you must not lose.
router.post sends one action that has no fields. useForm is the right call when the user is typing.
The progress bar is a setting on createInertiaApp. A second progress library fights the one the kit starts.
preserveScroll stops a filter or a save from jumping to the top of a long Vue page.
preserveState leaves the current component in place. Validation errors stick around if you force it on after a save.
onSuccess can reset one field after a save. Resetting before the redirect drops the value you meant to send.
A custom rule is a class with validate(). The Vue page still prints form.errors for that field.
The image rule checks the upload. Send it as POST with a file field, including when you are updating.
The confirmed rule checks password_confirmation. The Vue form needs that second field name.
Rule::unique()->ignore() lets the signed-in user keep their email. A plain unique rule fails on save.
Move Inertia form rules into a form request and pass validated() to the model. authorize() still runs.
The Inertia Form component posts the fields and exposes errors. Wayfinder can supply the action.
Put the Laravel validation message under the matching Vue input. Inertia already sends form.errors.
Add Laravel Boost to a Vue and Inertia app so Cursor or Claude follows the Inertia Vue guidelines for the versions you have installed.
Install laravel/ai, prompt one agent from a controller, and show the reply on an Inertia Vue page. The provider key stays on the server.
Three ways to put Vue on a Laravel 13 app, and the one to pick when the team, the API, and the first feature are still small.
The Vue starter kit already keeps one layout alive between pages. Wrapping each page in AppLayout builds a new shell every time.
Old tutorials install Ziggy and call route(). The Vue starter kit generates Wayfinder functions. Do not install both.
The starter kit already appends the app name. Set VITE_APP_NAME, then give each page a title and one meta description.
Inertia 3 can flash a toast for one visit. The Vue starter kit already displays it. A Laravel with() call does not reach that toast.
Dispatch the job, store pending, ready, or failed, and reload that row in the Vue page until the file can be downloaded.
Routes, a login redirect, and a guest guard for a Vue app that is not Inertia. Load the user before the router runs.
Sanctum is the cookie and the API token. Passport is the OAuth server for a third party that brings its own client id.
Both packages still install. laravel new no longer offers them. The Vue starter kit already has the screens those tutorials are trying to publish.
The sort column is an allow-list. The direction is asc or desc. The database orders the rows, and the existing paginator links keep the query string.
Livewire keeps the UI in Blade. Inertia keeps it in Vue and leaves the routes in Laravel. The starter kit you pick is the decision.
The starter kit already runs Vite. The pieces people break are the @ alias, a secret in a VITE_ variable, and a public/hot file left on the server.
Public pages that need HTML on the first response use the SSR bundle and a process that stays up. An app that is entirely behind login can skip it.
A policy decides whether this user can update this post. The Vue page receives that boolean. The controller still calls authorize.
defineProps on the Inertia page is the type of the controller payload. A missing key fails the check on your machine, before a blank v-for in the browser.
A notification written to the database is a prop on the next Inertia visit. Live delivery is the Reverb guide, not a second copy of Echo.
Wayfinder writes a TypeScript function per controller action. The Inertia form takes that function. You do not add a second router.
The GitHub button is a normal link. The callback reads the Socialite user, finds or creates a local user, and logs that session in.
A resource is the JSON shape a SPA or Nuxt reads. An Inertia page can keep passing the model. Axios sees the rows on response.data.data.
router.reload can ask Laravel for one prop. Deferred props wait until after the first paint. Merge appends on those partial visits only.
Nuxt renders the public site. Laravel stays the API. The cookie rules stay in the Sanctum guide. This page is the Nuxt call.
Inertia's Form component already validates one field as the user leaves it. A precognitive request runs the rules and does not run the controller.
Spatie stores the permission. The controller still checks it. The Vue page only receives a boolean, so a hidden button is not the authorization.
Laravel holds the strings. vue-i18n renders them. The instance is created once, so a locale change has to push new messages into it.
Pest hits the Laravel routes. Vitest and Vue Test Utils hit one component. The starter kit does not ship a Vitest script until you add one.
Sail runs PHP, the database, and npm in Docker. The Vue dev server is a Sail command, and HMR fails when the browser cannot see that container.
The Vue starter kit already ships Fortify screens. Install Fortify when the front end is a separate SPA, or when those actions need a new field.
A same-origin Inertia app does not need CORS. A separate SPA does, and a 419 is a different failure from a blocked response.
paginate, simplePaginate, and cursorPaginate return different JSON. The Vue page can only render the links that object actually has.
Create, edit, and delete posts from a Vue page, with validation errors, search, and pagination. Continues the official Vue starter kit.
Cookie sessions for a first-party Vue app, CSRF, stateful domains, and the token path that is only for a phone or a third party.
The production build Laravel Cloud documents, the Inertia SSR bundle, the queue, and the public/hot file that breaks a live site.
Install Reverb, broadcast a public event through the queue, and subscribe from a Vue page with useEchoPublic.
Send an image with the Inertia Form component, validate it, store it on the public disk, and show upload progress.
Page props already hold the posts. Pinia is for client state that should survive the next visit, such as a draft.
Laravel 13.35 adds Route::query() for the new HTTP QUERY method, response()->markdown(), Schedule::alwaysOnOneServer(), percentage memory limits for queue workers, array-based fake assertions and an opt-in defaults() method for Eloquent models. Before and after code for each.
Inertia 3.9 adds a background option for usePoll, a type field on the navigate event so you can refresh stale pages after the back button, and 3.9.1 ships two security hardening fixes. Here is what changed, with Vue 3 examples.
Build two everyday features with Inertia 3's new tools: a like button that updates instantly and rolls back on failure, and a debounced live search with useHttp. Laravel 13 back end, typed Vue 3 front end, tests included.
Inertia 2 stopped getting bug fixes on September 26, 2026. Here is the step-by-step upgrade we tested on a real Laravel 13 + Vue app: dependencies, the new Vite plugin, renamed APIs, Axios, SSR and a final audit.
Laravel & VueJs started as an open source CMS built with Laravel, Nova, GraphQL and Nuxt. Here is what it was, what it is now, and how you can help shape it.
Our mission is to help web developers level up with Laravel and Vue. Here is how to join the community, follow our channels, advertise, or write for the blog.
PHP versions, extensions, Node.js, databases and the packages worth knowing: a practical checklist for a modern Laravel + Vue stack, and how it compares with the stack this site started on.
Create a project with the official Vue starter kit, then add your first model, controller and typed Vue page with Inertia. Every command you need, start to finish.
What we shipped with the move to laravel-vuejs.space, and what comes next: more tutorials, a newsletter, a better jobs board and an easier way to contribute.
Topics
From your first route to production, and from your first component to a full single-page app.

Back-end development with Laravel: routing, Eloquent, queues, testing and deployment.
159 articles
Front-end development with Vue 3: components, the Composition API, Inertia and Nuxt.
153 articles
Careers, freelancing and hiring in the Laravel and Vue ecosystem: finding work, pricing and remote jobs.
Coming soon
Long-form guides and books for Laravel and Vue developers, from first app to production-ready architecture.
Coming soonThe stack we write about
The same stack, compared with 2019, is on Then and now. Where each file goes in a new Vue starter kit is on Where it goes. The October 2026 release notes are on What shipped. The machine checks and the commands in order are on Start here. The lines the setup guide shows are on Samples.
Jobs
Post your role to a community that writes this stack every day. Free in the jobs group, or featured across our page, channels and newsletter. Every listing is reviewed by a human.

Community
Ask questions, share what you are building and find your next gig. Laravel and Vue developers from all over the world are already there.
Newsletter
One short email with new guides, release notes worth reading and hand-picked community links. No spam, unsubscribe any time.