In a Laravel app, what do laravel-vite-plugin's refresh option and the @viteReactRefresh Blade directive each do, and when do you need them?
answer
- Blade has no module to hot-swap
- refresh: true means full page reload
- default watch paths include resources/views
- React preamble normally lives in index.html
- @viteReactRefresh goes before @vite
basics
~20 sThe refresh option makes the dev server trigger a full page reload when Blade views, routes, lang files or Livewire classes change. @viteReactRefresh injects React Fast Refresh's preamble into Laravel's HTML during npm run dev and must precede @vite.
solid answer
~40 sHot module replacement only works for modules in Vite's graph; a Blade view is rendered by PHP, so there is nothing to hot-swap. `refresh: true` adds a full-reload watcher (built on vite-plugin-full-reload) over the default paths — `app/Livewire/**`, `app/View/Components/**`, `lang/**`, `resources/lang/**`, `resources/views/**`, `routes/**` — and you can pass your own paths or a `{ paths, config }` object instead. `@viteReactRefresh` exists because `@vitejs/plugin-react` normally injects its Fast Refresh preamble into the `index.html` Vite serves, but in Laravel, PHP serves the HTML. The directive prints that inline module script, importing `@react-refresh` from the dev server, only while the hot file exists, and it must come before `@vite`. Vue and Svelte need no equivalent.
code
javascript · 13 linesimport { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.tsx'],
refresh: ['resources/views/**', 'routes/**'],
}),
react(),
],
});go deeper
Know that refresh: true reloads the browser on Blade changes and that React apps put @viteReactRefresh before @vite.
Explain why both exist: Laravel serves the HTML, so Blade is outside HMR and React's preamble is never injected by Vite.
Tune the watch paths for large apps and recognise the missing-preamble error when a layout's directive order is wrong.
Weigh developer-loop speed as a stack concern: full reloads on Blade versus component-level hot updates in React or Vue front ends.
## Two dev-only helpers for two different gaps Both features exist because in a Laravel app **Laravel serves the HTML and Vite serves the modules**. Vite's own conveniences assume Vite owns the page; each helper fills one gap that assumption leaves. ## `refresh`: reload the page when server-rendered files change **Hot module replacement (HMR)** swaps a changed JavaScript or CSS module in the running page. A Blade view is not a module: PHP renders it into HTML on each request, and the dev server never sees it. Editing `resources/views/dashboard.blade.php` therefore changes nothing on screen until you press reload. The `refresh` option closes that gap with a **full page reload** when watched files change: | Value | Effect | |---|---| | omitted or `false` (the plugin default) | no watcher | | `true` | watch the default paths below | | a string or array of globs | watch exactly those paths | | `{ paths, config }` or an array of them | watch the paths with options for the underlying watcher, e.g. `config: { delay: 300 }` | With `true`, the plugin watches whichever of these exist in the project: - `app/Livewire/**` - `app/View/Components/**` - `lang/**` and `resources/lang/**` - `resources/views/**` - `routes/**` — useful when a route-name generator feeds the front end Under the hood the plugin wraps **vite-plugin-full-reload**. The Laravel 13 skeleton's `vite.config.js` already sets `refresh: true` and additionally tells the dev server's watcher to ignore `storage/framework/views`, where Blade's compiled PHP lives, so rendering a page does not itself look like a change. The option is **dev-only** in effect: `npm run build` produces the same files with or without it. ## `@viteReactRefresh`: React Fast Refresh without Vite's HTML `@vitejs/plugin-react` implements **Fast Refresh**, which keeps component state while swapping edited components. It needs a small **preamble** script to run before any React code: it imports the refresh runtime and installs global hooks. In a plain Vite app the React plugin injects that preamble into the `index.html` the dev server serves. A Laravel page's HTML comes from Blade, so nothing injects it, and React components fail to hot-update. `@viteReactRefresh` prints it: - It compiles to `app(Vite::class)->reactRefresh()`. - **Only when hot**: if the hot file does not exist it returns nothing, so the production page carries no dev script. - It emits an inline `<script type="module">` that imports `RefreshRuntime` from `<dev server>/@react-refresh`, calls `injectIntoGlobalHook(window)` and sets the flags the React plugin checks. - It carries the CSP nonce when one was set with `Vite::useCspNonce()`. - **Order matters**: it must come **before** `@vite`, because the preamble has to run before the entry imports React components. ```html <head> @viteReactRefresh @vite(['resources/css/app.css', 'resources/js/app.tsx']) </head> ``` The official React starter kit's root template does exactly this. Vue's and Svelte's Vite plugins do not need an HTML preamble, so there is no `@viteVueRefresh`. ## Where each one lives | | `refresh` | `@viteReactRefresh` | |---|---|---| | Configured in | `vite.config.js`, inside `laravel({...})` | the root Blade layout | | Runs on | the dev server (a file watcher) | the browser (an inline script) | | Needed for | Blade, Livewire, routes, translations | React components only | | Effect in production | none | prints nothing | Livewire apps benefit most from `refresh`: their components are PHP classes plus Blade views, so every UI edit is a server-side change that only a reload can show. ## Common mistakes 1. Expecting HMR for Blade edits and blaming the dev server when nothing updates — the fix is `refresh`, and the result is a reload, not a hot swap. 2. Placing `@viteReactRefresh` after `@vite`, so the React plugin's check for the preamble flag fails when the first component module loads. 3. Adding `@viteReactRefresh` to a Vue app, where it does nothing useful. 4. Worrying that it leaks into production — it prints nothing without a hot file. 5. Watching huge directories with custom `refresh` globs, which reloads the page on unrelated saves. ## How to answer State the gap first (Laravel owns the HTML), then the two fixes: a full reload for server-rendered files, and a preamble for React Fast Refresh. Mention the default watch paths and the ordering rule — those are the details interviewers check.
- Does refresh: true slow down or change the production build?No. The full-reload watcher only runs inside the dev server; `npm run build` emits the same hashed files and manifest whether `refresh` is set or not.
- What happens if @viteReactRefresh is left in the layout after deploying a production build?Nothing. `reactRefresh()` checks `isRunningHot()` first and returns nothing when there is no hot file, so production HTML contains no preamble script.
saying these in an interview costs you the question
- HMR hot-swaps Blade templates just like JavaScript modules
- refresh defaults to true, so you never need to set it
- @viteReactRefresh can go anywhere in the head, even after @vite
- Vue apps also need @viteReactRefresh for hot updates
- @viteReactRefresh prints its script in production too