Vite has been around for some time now, but it's still a hassle to set up on frameworks that is not set up with vite out of the box.
In this article, I will guide you through the process of installing Vite on a Craft CMS installation that was using another CSS / JS precompiler.
DDev setup
Exposing the vite dev server port
Vites HMR (Hot Module Replacement) service is provided by the vite dev server that you will be running while development.
To allow access from your web browser to the port that the Vite dev server will be running on inside the DDev Docker container, you will need to expose this port on the container.
This is done by adding a new dict to your .ddev/config.yaml file.
...
web_extra_exposed_ports:
- name: vite
container_port: 3000
http_port: 3000
https_port: 3001
...
Save the YAML file, and restart the DDev project to apply the changes.
ddev restart
.env
Environment variable setup
To manage most of my environmental setup, I like to store them along with secrets in the .env file of my project. To make everything work with both NPM and PHP, the following variables need to be included.
...
VITE_PORT_HTTP=3000
VITE_PORT_HTTPS=3001
PRIMARY_SITE_URL="https://haxor.no.ddev.site"
...
Craft Plugin
All the stuff Vite needs for the HMR to work with changes in twig templates, and the use of the dev server in your dev environment vs static assets in your prod environment, is handled by the Vite Craft Plugin by NY Studio 107. Install it from the Craft plugin store.
Once installed, you need to create a "dist" folder inside the webroot directory. This will store the built CSS and JS for your project.
The configuration for the Vite plugin can be stored in any custom config file, but to keep it clean and easy to maintain, I added a new dedicated file to my config directory.
<?php
use craft\helpers\App;
// Use the current host for dev server requests. CLI fallback to primary site URL
$host = Craft::$app->getRequest()->getIsConsoleRequest()
? App::env('PRIMARY_SITE_URL')
: Craft::$app->getRequest()->getHostInfo();
$port = App::env('VITE_PORT_HTTPS', 3001);
return [
'devServerPublic' => "$host:$port",
'serverPublic' => '/dist',
'useDevServer' => App::env('CRAFT_ENVIRONMENT') === 'dev',
'manifestPath' => '@webroot/dist/manifest.json',
];
Twig implementation
How to use built asstes in prod and dev server in dev
Thankfully, it's very easy to implement the assets built by Vite in the Twig templates. Insert these snippets inside the templates where you usually define CSS and JS.
<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/css2?family=Oswald:wght@500&display=swap" rel="stylesheet">
</head>
<body>
{{ craft.vite.script("resources/js/site.js") }}
{% if devMode %}
<script type="module" src="https://haxor.no.ddev.site:3001/@vite/client"></script>
{% endif %}
</body>
</html>
JavaScript setup
For ddev to work with my setup, you will need a couple of NPM packages. Install them with these commands
ddev npm install vite vite-plugin-live-reload --save-dev
ddev npm install dotenv --save-dev
# Install this if you use vue.js in your project
ddev npm install @vitejs/plugin-vue --save-dev
To run vite using npm scripts, use this commands in the package.json file.
{
...
"scripts": {
"dev": "vite",
"prod": "vite build"
},
...
}
Vite config
My project is structured so that all the JS and SCSS source files are stored in the resources directory within my project root. like this:
tree resources
resources
├── js
│ ├── components
│ │ ├── accordian.js
│ │ ├── chapterHighlight.js
│ │ └── ...
│ └── site.js
└── scss
├── components
│ ├── header.scss
│ ├── footer.scss
│ ├── ...
└── site.scss
As previously stated, I want the output from Vite to be stored in the web/dist directory.
The vite config file can look like this:
import { defineConfig } from "vite";
import vue from '@vitejs/plugin-vue'
import liveReload from 'vite-plugin-live-reload';
import dotenv from 'dotenv';
import fs from 'fs';
export default ({ mode }) => {
Object.assign(process.env, dotenv.parse(fs.readFileSync(`${__dirname}/.env`)));
const port = process.env.VITE_PORT_HTTP;
const origin = `${process.env.PRIMARY_SITE_URL}`;
return defineConfig({
plugins: [
vue(),
liveReload([
__dirname + '/templates/**/*.twig',
]),
],
build: {
manifest: "manifest.json",
outDir: 'web/dist/',
rollupOptions: {
input: {
js: './resources/js/site.js',
css: './resources/scss/site.scss',
},
},
},
server: {
cors: true,
host: '0.0.0.0',
port: port,
strictPort: true,
// Defines the origin of the CSS asset URLs during development
origin: origin,
proxy: {
'/resources/svg': {
target: 'http://localhost/resources/svg/',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/resources\/svg/, ''),
},
},
},
})
}
If your project is structured in another way, you must define the correct "main" JS in line 25 and the "main" SCSS file in line 26.
If you want to store the output in another directory, you must change the outDir in line 22. Please be aware that Vite deletes all files in that directory when building for production, so it can only contain Vite build files.
Since the Vite dev server will point any CSS url() to a location on the dev server, you must use a proxy for the resources. In this example, I have several SVGs I use for icons as background images. These are stored in the /web/resources/svg directory. For them to function in the dev environment, I had to set up a proxy.
Dev environement
How to run Vite in ddev in your dev environment
To run this setup with a functioning ddev setup, change the environment to "dev", and then start your ddev project and run the NPM dev script.
...
ENVIRONMENT="dev"
...
ddev start
ddev npm run dev
Prod environment
How to build for your prod environment and use the build files.
In the staging or production environment you must have the correct environement set. If it's set to anything other than dev, the Vite craft plugin will make use of the build files, and not anything served by the dev server.
...
ENVIRONMENT="prod"
...
npm run prod
Congratulations, you now have a working Vite setup for your Craft CMS projects. 🎉
Enjoy the "blazingly fast" dev experience!