Introduction
Vite is a modern frontend build tool that provides an extremely fast development environment and bundles your code for production. When building applications with Laravel, you will typically use Vite to bundle your application's CSS and JavaScript files into production ready assets.
Laravel integrates seamlessly with Vite by providing an official plugin and Blade directive to load your assets for development and production.
Note:
Are you running Laravel Mix? Vite has replaced Laravel Mix in new Laravel installations. For Mix documentation, please visit the Laravel Mix website. If you would like to switch to Vite, please see our migration guide.
Choosing Between Vite and Laravel Mix
Before transitioning to Vite, new Laravel applications utilized Mix, which is powered by webpack, when bundling assets. Vite focuses on providing a faster and more productive experience when building rich JavaScript applications. If you are developing a Single Page Application (SPA), including those developed with tools like Inertia, Vite will be the perfect fit.
Vite also works well with traditional server-side rendered applications with JavaScript "sprinkles", including those using Livewire. However, it lacks some features that Laravel Mix supports, such as the ability to copy arbitrary assets into the build that are not referenced directly in your JavaScript application.
Migrating Back to Mix
Have you started a new Laravel application using our Vite scaffolding but need to move back to Laravel Mix and webpack? No problem. Please consult our official guide on migrating from Vite to Mix.
Installation & Setup
Note:
The following documentation discusses how to manually install and configure the Laravel Vite plugin. However, Laravel's starter kits already include all of this scaffolding and are the fastest way to get started with Laravel and Vite.
Installing Node
You must ensure that Node.js (16 ) and NPM are installed before running Vite and the Laravel plugin:
node -v
npm -v
You can easily install the latest version of Node and NPM using simple graphical installers from the official Node website. Or, if you are using Laravel Sail, you may invoke Node and NPM through Sail:
./vendor/bin/sail node -v
./vendor/bin/sail npm -v
Installing Vite and the Laravel Plugin
Within a fresh installation of Laravel, you will find a
package.json
file in the root of your
application's directory structure. The default
package.json
file already includes
everything you need to get started using Vite and the
Laravel plugin. You may install your application's
frontend dependencies via NPM:
npm install
Configuring Vite
Vite is configured via a vite.config.js
file
in the root of your project. You are free to customize
this file based on your needs, and you may also install
any other plugins your application requires, such as
@vitejs/plugin-vue
or
@vitejs/plugin-react
.
The Laravel Vite plugin requires you to specify the entry points for your application. These may be JavaScript or CSS files, and include preprocessed languages such as TypeScript, JSX, TSX, and Sass.
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel([
'resources/css/app.css',
'resources/js/app.js',
]),
],
});
If you are building an SPA, including applications built using Inertia, Vite works best without CSS entry points:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel([
'resources/css/app.css', // [tl! remove]
'resources/js/app.js',
]),
],
});
Instead, you should import your CSS via JavaScript.
Typically, this would be done in your application's
resources/js/app.js
file:
import './bootstrap';
import '../css/app.css'; // [tl! add]
The Laravel plugin also supports multiple entry points and advanced configuration options such as SSR entry points.
Working With a Secure Development Server
If your local development web server is serving your application via HTTPS, you may run into issues connecting to the Vite development server.
If you are using Laravel Herd and have secured the site or you are using Laravel Valet and have run the secure command against your application, the Laravel Vite plugin will automatically detect and use the generated TLS certificate for you.
If you secured the site using a host that does not match
the application's directory name, you may manually
specify the host in your application's
vite.config.js
file:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
// ...
detectTls: 'my-app.test', // [tl! add]
}),
],
});
When using another web server, you should generate a trusted certificate and manually configure Vite to use the generated certificates:
// ...
import fs from 'fs'; // [tl! add]
const host = 'my-app.test'; // [tl! add]
export default defineConfig({
// ...
server: { // [tl! add]
host, // [tl! add]
hmr: { host }, // [tl! add]
https: { // [tl! add]
key: fs.readFileSync(`/path/to/${host}.key`), // [tl! add]
cert: fs.readFileSync(`/path/to/${host}.crt`), // [tl! add]
}, // [tl! add]
}, // [tl! add]
});
If you are unable to generate a trusted certificate for
your system, you may install and configure the @vitejs/plugin-basic-ssl
plugin. When using untrusted certificates, you
will need to accept the certificate warning for Vite's
development server in your browser by following the
"Local" link in your console when running the
npm run dev
command.
Running the Development Server in Sail on WSL2
When running the Vite development server within Laravel Sail on Windows
Subsystem for Linux 2 (WSL2), you should add the
following configuration to your
vite.config.js
file to ensure the browser
can communicate with the development server:
// ...
export default defineConfig({
// ...
server: { // [tl! add:start]
hmr: {
host: 'localhost',
},
}, // [tl! add:end]
});
If your file changes are not being reflected in the
browser while the development server is running, you may
also need to configure Vite's server.watch.usePolling
option.
Loading Your Scripts and Styles
With your Vite entry points configured, you may now
reference them in a @vite()
Blade directive
that you add to the <head>
of your
application's root template:
<!doctype html>
<head>
{{-- ... --}}
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
If you're importing your CSS via JavaScript, you only need to include the JavaScript entry point:
<!doctype html>
<head>
{{-- ... --}}
@vite('resources/js/app.js')
</head>
The @vite
directive will automatically
detect the Vite development server and inject the Vite
client to enable Hot Module Replacement. In build mode,
the directive will load your compiled and versioned
assets, including any imported CSS.
If needed, you may also specify the build path of your
compiled assets when invoking the @vite
directive:
<!doctype html>
<head>
{{-- Given build path is relative to public path. --}}
@vite('resources/js/app.js', 'vendor/courier/build')
</head>
Inline Assets
Sometimes it may be necessary to include the raw content
of assets rather than linking to the versioned URL of
the asset. For example, you may need to include asset
content directly into your page when passing HTML
content to a PDF generator. You may output the content
of Vite assets using the content
method
provided by the Vite
facade:
@php
use Illuminate\Support\Facades\Vite;
@endphp
<!doctype html>
<head>
{{-- ... --}}
<style>
{!! Vite::content('resources/css/app.css') !!}
</style>
<script>
{!! Vite::content('resources/js/app.js') !!}
</script>
</head>
Running Vite
There are two ways you can run Vite. You may run the
development server via the dev
command,
which is useful while developing locally. The
development server will automatically detect changes to
your files and instantly reflect them in any open
browser windows.
Or, running the build
command will version
and bundle your application's assets and get them ready
for you to deploy to production:
# Run the Vite development server...
npm run dev
# Build and version the assets for production...
npm run build
If you are running the development server in Sail on WSL2, you may need some additional configuration options.
Working With JavaScript
Aliases
By default, The Laravel plugin provides a common alias to help you hit the ground running and conveniently import your application's assets:
{
'@' => '/resources/js'
}
You may overwrite the '@'
alias by adding
your own to the vite.config.js
configuration file:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel(['resources/ts/app.tsx']),
],
resolve: {
alias: {
'@': '/resources/ts',
},
},
});
Vue
If you would like to build your frontend using the Vue framework, then
you will also need to install the
@vitejs/plugin-vue
plugin:
npm install --save-dev @vitejs/plugin-vue
You may then include the plugin in your
vite.config.js
configuration file. There
are a few additional options you will need when using
the Vue plugin with Laravel:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [
laravel(['resources/js/app.js']),
vue({
template: {
transformAssetUrls: {
// The Vue plugin will re-write asset URLs, when referenced
// in Single File Components, to point to the Laravel web
// server. Setting this to `null` allows the Laravel plugin
// to instead re-write asset URLs to point to the Vite
// server instead.
base: null,
// The Vue plugin will parse absolute URLs and treat them
// as absolute paths to files on disk. Setting this to
// `false` will leave absolute URLs un-touched so they can
// reference assets in the public directory as expected.
includeAbsolute: false,
},
},
}),
],
});
Note:
Laravel's starter kits already include the proper Laravel, Vue, and Vite configuration. Check out Laravel Breeze for the fastest way to get started with Laravel, Vue, and Vite.
React
If you would like to build your frontend using the React framework,
then you will also need to install the
@vitejs/plugin-react
plugin:
npm install --save-dev @vitejs/plugin-react
You may then include the plugin in your
vite.config.js
configuration file:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [
laravel(['resources/js/app.jsx']),
react(),
],
});
You will need to ensure that any files containing JSX
have a .jsx
or .tsx
extension,
remembering to update your entry point, if required, as
shown above.
You will also need to include the additional
@viteReactRefresh
Blade directive alongside
your existing @vite
directive.
@viteReactRefresh
@vite('resources/js/app.jsx')
The @viteReactRefresh
directive must be
called before the @vite
directive.
Note:
Laravel's starter kits already include the proper Laravel, React, and Vite configuration. Check out Laravel Breeze for the fastest way to get started with Laravel, React, and Vite.
Inertia
The Laravel Vite plugin provides a convenient
resolvePageComponent
function to help you
resolve your Inertia page components. Below is an
example of the helper in use with Vue 3; however, you
may also utilize the function in other frameworks such
as React:
import { createApp, h } from 'vue';
import { createInertiaApp } from '@inertiajs/vue3';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
createInertiaApp({
resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
setup({ el, App, props, plugin }) {
return createApp({ render: () => h(App, props) })
.use(plugin)
.mount(el)
},
});
Note:
Laravel's starter kits already include the proper Laravel, Inertia, and Vite configuration. Check out Laravel Breeze for the fastest way to get started with Laravel, Inertia, and Vite.
URL Processing
When using Vite and referencing assets in your application's HTML, CSS, or JS, there are a couple of caveats to consider. First, if you reference assets with an absolute path, Vite will not include the asset in the build; therefore, you should ensure that the asset is available in your public directory.
When referencing relative asset paths, you should remember that the paths are relative to the file where they are referenced. Any assets referenced via a relative path will be re-written, versioned, and bundled by Vite.
Consider the following project structure:
public/
taylor.png
resources/
js/
Pages/
Welcome.vue
images/
abigail.png
The following example demonstrates how Vite will treat relative and absolute URLs:
<!-- This asset is not handled by Vite and will not be included in the build -->
<img src="/taylor.png">
<!-- This asset will be re-written, versioned, and bundled by Vite -->
<img src="../../images/abigail.png">
Working With Stylesheets
You can learn more about Vite's CSS support within the Vite
documentation. If you are using PostCSS plugins
such as Tailwind,
you may create a postcss.config.js
file in
the root of your project and Vite will automatically
apply it:
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
Note:
Laravel's starter kits already include the proper Tailwind, PostCSS, and Vite configuration. Or, if you would like to use Tailwind and Laravel without using one of our starter kits, check out Tailwind's installation guide for Laravel.
Working With Blade and Routes
Processing Static Assets With Vite
When referencing assets in your JavaScript or CSS, Vite automatically processes and versions them. In addition, when building Blade based applications, Vite can also process and version static assets that you reference solely in Blade templates.
However, in order to accomplish this, you need to make
Vite aware of your assets by importing the static assets
into the application's entry point. For example, if you
want to process and version all images stored in
resources/images
and all fonts stored in
resources/fonts
, you should add the
following in your application's
resources/js/app.js
entry point:
import.meta.glob([
'../images/**',
'../fonts/**',
]);
These assets will now be processed by Vite when running
npm run build
. You can then reference these
assets in Blade templates using the
Vite::asset
method, which will return the
versioned URL for a given asset:
<img src="{{ Vite::asset('resources/images/logo.png') }}">
Refreshing on Save
When your application is built using traditional
server-side rendering with Blade, Vite can improve your
development workflow by automatically refreshing the
browser when you make changes to view files in your
application. To get started, you can simply specify the
refresh
option as true
.
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
// ...
refresh: true,
}),
],
});
When the refresh
option is
true
, saving files in the following
directories will trigger the browser to perform a full
page refresh while you are running npm run
dev
:
app/View/Components/**
lang/**
resources/lang/**
resources/views/**
routes/**
Watching the routes/**
directory is useful
if you are utilizing Ziggy to
generate route links within your application's
frontend.
If these default paths do not suit your needs, you can specify your own list of paths to watch:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
// ...
refresh: ['resources/views/**'],
}),
],
});
Under the hood, the Laravel Vite plugin uses the vite-plugin-full-reload
package, which offers some advanced configuration
options to fine-tune this feature's behavior. If you
need this level of customization, you may provide a
config
definition:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
// ...
refresh: [{
paths: ['path/to/watch/**'],
config: { delay: 300 }
}],
}),
],
});
Aliases
It is common in JavaScript applications to create aliases to regularly
referenced directories. But, you may also create aliases
to use in Blade by using the macro
method
on the Illuminate\Support\Facades\Vite
class. Typically, "macros" should be defined
within the boot
method of a service provider:
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Vite::macro('image', fn (string $asset) => $this->asset("resources/images/{$asset}"));
}
Once a macro has been defined, it can be invoked within
your templates. For example, we can use the
image
macro defined above to reference an
asset located at
resources/images/logo.png
:
<img src="{{ Vite::image('logo.png') }}" alt="Laravel Logo">
Custom Base URLs
If your Vite compiled assets are deployed to a domain
separate from your application, such as via a CDN, you
must specify the ASSET_URL
environment
variable within your application's .env
file:
ASSET_URL=https://cdn.example.com
After configuring the asset URL, all re-written URLs to your assets will be prefixed with the configured value:
https://cdn.example.com/build/assets/app.9dce8d17.js
Remember that absolute URLs are not re-written by Vite, so they will not be prefixed.
Environment Variables
You may inject environment variables into your JavaScript
by prefixing them with VITE_
in your
application's .env
file:
VITE_SENTRY_DSN_PUBLIC=http://example.com
You may access injected environment variables via the
import.meta.env
object:
import.meta.env.VITE_SENTRY_DSN_PUBLIC
Disabling Vite in Tests
Laravel's Vite integration will attempt to resolve your assets while running your tests, which requires you to either run the Vite development server or build your assets.
If you would prefer to mock Vite during testing, you may
call the withoutVite
method, which is
available for any tests that extend Laravel's
TestCase
class:
use Tests\TestCase;
class ExampleTest extends TestCase
{
public function test_without_vite_example(): void
{
$this->withoutVite();
// ...
}
}
If you would like to disable Vite for all tests, you may
call the withoutVite
method from the
setUp
method on your base
TestCase
class:
<?php
namespace Tests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
protected function setUp(): void// [tl! add:start]
{
parent::setUp();
$this->withoutVite();
}// [tl! add:end]
}
Server-Side Rendering (SSR)
The Laravel Vite plugin makes it painless to set up
server-side rendering with Vite. To get started, create
an SSR entry point at resources/js/ssr.js
and specify the entry point by passing a configuration
option to the Laravel plugin:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
input: 'resources/js/app.js',
ssr: 'resources/js/ssr.js',
}),
],
});
To ensure you don't forget to rebuild the SSR entry
point, we recommend augmenting the "build"
script in your application's package.json
to create your SSR build:
"scripts": {
"dev": "vite",
"build": "vite build" // [tl! remove]
"build": "vite build && vite build --ssr" // [tl! add]
}
Then, to build and start the SSR server, you may run the following commands:
npm run build
node bootstrap/ssr/ssr.js
If you are using SSR
with Inertia, you may instead use the
inertia:start-ssr
Artisan command to start
the SSR server:
php artisan inertia:start-ssr
Note:
Laravel's starter kits already include the proper Laravel, Inertia SSR, and Vite configuration. Check out Laravel Breeze for the fastest way to get started with Laravel, Inertia SSR, and Vite.
Script and Style Tag Attributes
Content Security Policy (CSP) Nonce
If you wish to include a nonce
attribute on your script and style tags as part
of your Content
Security Policy, you may generate or specify a
nonce using the useCspNonce
method within a
custom middleware:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Vite;
use Symfony\Component\HttpFoundation\Response;
class AddContentSecurityPolicyHeaders
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
Vite::useCspNonce();
return $next($request)->withHeaders([
'Content-Security-Policy' => "script-src 'nonce-".Vite::cspNonce()."'",
]);
}
}
After invoking the useCspNonce
method,
Laravel will automatically include the
nonce
attributes on all generated script
and style tags.
If you need to specify the nonce elsewhere, including the
Ziggy
@route
directive included with
Laravel's starter kits,
you may retrieve it using the cspNonce
method:
@routes(nonce: Vite::cspNonce())
If you already have a nonce that you would like to
instruct Laravel to use, you may pass the nonce to the
useCspNonce
method:
Vite::useCspNonce($nonce);
Subresource Integrity (SRI)
If your Vite manifest includes integrity
hashes for your assets, Laravel will automatically add
the integrity
attribute on any script and
style tags it generates in order to enforce Subresource
Integrity. By default, Vite does not include the
integrity
hash in its manifest, but you may
enable it by installing the vite-plugin-manifest-sri
NPM plugin:
npm install --save-dev vite-plugin-manifest-sri
You may then enable this plugin in your
vite.config.js
file:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import manifestSRI from 'vite-plugin-manifest-sri';// [tl! add]
export default defineConfig({
plugins: [
laravel({
// ...
}),
manifestSRI(),// [tl! add]
],
});
If required, you may also customize the manifest key where the integrity hash can be found:
use Illuminate\Support\Facades\Vite;
Vite::useIntegrityKey('custom-integrity-key');
If you would like to disable this auto-detection
completely, you may pass false
to the
useIntegrityKey
method:
Vite::useIntegrityKey(false);
Arbitrary Attributes
If you need to include additional attributes on your
script and style tags, such as the data-turbo-track
attribute, you may specify them via the
useScriptTagAttributes
and
useStyleTagAttributes
methods. Typically,
this methods should be invoked from a service provider:
use Illuminate\Support\Facades\Vite;
Vite::useScriptTagAttributes([
'data-turbo-track' => 'reload', // Specify a value for the attribute...
'async' => true, // Specify an attribute without a value...
'integrity' => false, // Exclude an attribute that would otherwise be included...
]);
Vite::useStyleTagAttributes([
'data-turbo-track' => 'reload',
]);
If you need to conditionally add attributes, you may pass a callback that will receive the asset source path, its URL, its manifest chunk, and the entire manifest:
use Illuminate\Support\Facades\Vite;
Vite::useScriptTagAttributes(fn (string $src, string $url, array|null $chunk, array|null $manifest) => [
'data-turbo-track' => $src === 'resources/js/app.js' ? 'reload' : false,
]);
Vite::useStyleTagAttributes(fn (string $src, string $url, array|null $chunk, array|null $manifest) => [
'data-turbo-track' => $chunk && $chunk['isEntry'] ? 'reload' : false,
]);
Warning!
The$chunk
and$manifest
arguments will benull
while the Vite development server is running.
Advanced Customization
Out of the box, Laravel's Vite plugin uses sensible
conventions that should work for the majority of
applications; however, sometimes you may need to
customize Vite's behavior. To enable additional
customization options, we offer the following methods
and options which can be used in place of the
@vite
Blade directive:
<!doctype html>
<head>
{{-- ... --}}
{{
Vite::useHotFile(storage_path('vite.hot')) // Customize the "hot" file...
->useBuildDirectory('bundle') // Customize the build directory...
->useManifestFilename('assets.json') // Customize the manifest filename...
->withEntryPoints(['resources/js/app.js']) // Specify the entry points...
->createAssetPathsUsing(function (string $path, ?bool $secure) { // Customize the backend path generation for built assets...
return "https://cdn.example.com/{$path}";
})
}}
</head>
Within the vite.config.js
file, you should
then specify the same configuration:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
hotFile: 'storage/vite.hot', // Customize the "hot" file...
buildDirectory: 'bundle', // Customize the build directory...
input: ['resources/js/app.js'], // Specify the entry points...
}),
],
build: {
manifest: 'assets.json', // Customize the manifest filename...
},
});
Correcting Dev Server URLs
Some plugins within the Vite ecosystem assume that URLs which begin with a forward-slash will always point to the Vite dev server. However, due to the nature of the Laravel integration, this is not the case.
For example, the vite-imagetools
plugin
outputs URLs like the following while Vite is serving
your assets:
<img src="/@imagetools/f0b2f404b13f052c604e632f2fb60381bf61a520">
The vite-imagetools
plugin is expecting that
the output URL will be intercepted by Vite and the
plugin may then handle all URLs that start with
/@imagetools
. If you are using plugins that
are expecting this behaviour, you will need to manually
correct the URLs. You can do this in your
vite.config.js
file by using the
transformOnServe
option.
In this particular example, we will prepend the dev
server URL to all occurrences of
/@imagetools
within the generated code:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import { imagetools } from 'vite-imagetools';
export default defineConfig({
plugins: [
laravel({
// ...
transformOnServe: (code, devServerUrl) => code.replaceAll('/@imagetools', devServerUrl '/@imagetools'),
}),
imagetools(),
],
});
Now, while Vite is serving Assets, it will output URLs that point to the Vite dev server:
- <img src="/@imagetools/f0b2f404b13f052c604e632f2fb60381bf61a520"><!-- [tl! remove] -->
<img src="http://[::1]:5173/@imagetools/f0b2f404b13f052c604e632f2fb60381bf61a520"><!-- [tl! add] -->