Vite
Vite is a modern build tool that significantly improves the development experience. Unlike traditional bundlers like webpack, Vite leverages native ES modules in the browser to enable faster builds and updates. During development, when using UI frameworks that support Hot Module Replacement (HMR) like Vue, React, or Svelte, your changes appear instantly in the browser without rebuilding the entire bundle or refreshing the page. For vanilla JavaScript or frameworks without HMR support, Vite still provides fast page reloads on changes. When building for production, Vite uses Rollup to create highly optimized assets.
For ApostropheCMS projects, this translates to faster development cycles, improved debugging with better source maps, and a more streamlined build process that requires less configuration. The dev server starts up instantly regardless of your application size, and HMR updates happen in milliseconds rather than seconds.
Using Vite in an ApostropheCMS project, rather than the default webpack build, is currently in beta. However, moving forward, webpack will slowly be deprecated. At this time we encourage you to test the new build in your dev
environment. If you want to take advantage of HMR in your project-level non-Admin UI code, we recommend you start any new project using ESM modules in your frontend code. You can see the steps to begin this migration in our documentation. While HMR will work out of the box for the Admin UI with hmr: 'apos'
, project-level code will need to use ESM syntax to get the full benefits of using Vite. Using CommonJS (e.g., require or module.exports) will break Vite builds due to incompatibility with Rollup, so it’s essential to switch to ESM syntax before using hrm: 'public'
.
Installation
Moving forward, all of our starter kits will have the Vite build option available. If you are creating a project from scratch without a starter kit, or want to enable Vite builds in an existing project you can install the package from npm.
npm install @apostrophecms/vite
The package then needs to be added to the app.js
file:
Core Features and Configuration
The Vite bundler for ApostropheCMS comes with sensible defaults while remaining highly configurable. Hot Module Replacement (HMR) is enabled out of the box for ESM project code, allowing you to see your changes instantly without a full page refresh.
Hot Module Replacement
HMR can be configured in three modes using the @apostrophecms/asset
options:
INFO
If you use the hmr: 'apos'
option, we recommend you do not pass the APOS_DEV
flag. The Vite build will selectively update altered code leading to a better development experience than APOS_DEV=1
, which upon process reload will force rebuilding of the apos
build. Vite is not using the build assets, so this doesn't make sense.
WebSocket Configuration
By default, the HMR WebSocket server runs on your ApostropheCMS server port, typically port 3000. For cases where you need a separate port (like running behind certain proxy configurations), you can configure a custom port:
Source Maps in Production
For debugging production builds, you can enable source maps to see the original source code in your browser's developer tools:
Default Public Build Configuration
While the apos
build (code in /ui/apos/
directories) is fully preconfigured, the public
build (code in /ui/src/
) comes with minimal configuration to give you more flexibility. Here's what's included by default:
Core Features:
- PostCSS plugin for essential ApostropheCMS features (e.g., "Breakpoint Preview" when enabled)
Modules/
alias for simplified module imports within/ui/src/
@/
alias for cross-module and cross-build access
Everything Else: For additional features or frameworks, you'll need to configure them yourself. Common examples include:
javascript// apos.vite.config.js import { defineConfig } from '@apostrophecms/vite/vite'; import vue from '@vitejs/plugin-vue'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [ // Add framework support vue(), // or react(), // Add other Vite plugins as needed imagemin({ // image optimization options }) ], css: { // Additional PostCSS plugins postcss: { plugins: [ autoprefixer(), tailwindcss() ] }, // Preprocessor options preprocessorOptions: { scss: { additionalData: `$theme: "light";` } } } });
Framework-Specific Configuration
When adding UI frameworks to your project, you'll need to:
Install the framework and its Vite plugin:
bash# For Vue npm install vue @vitejs/plugin-vue # For React npm install react react-dom @vitejs/plugin-react
Configure the plugin in your Vite config
Follow the framework's best practices for file organization within your
ui/src/
directory
TIP
Remember that while you have full control over the public build configuration, it's best to start minimal and add only what you need to keep your build process efficient.
Built-in Aliases
ApostropheCMS's Vite integration provides two powerful path aliases to simplify imports in your project:
The @/
Alias
The @/
alias is the recommended way to handle imports in your project. It is available for both public and admin UI builds and provides access to your entire project's source code. It follows the same path as your original source code but skips the ui/
part of the path.
// Current file: any file in any module inside of the `ui/` folder
// Actual path: modules/some-module/ui/src/lib/utils.js
import utils from '@/some-module/src/lib/utils.js';
// Actual path: modules/some-module/ui/apos/mixins/SomeMixin.js
import SomeMixin from '@/some-module/apos/mixins/SomeMixin.js';
WARNING
Important: When using the @/
alias, you should exercise caution sharing code between the public and Admin UI builds through the import of files from ui/apos
directories within files located in ui/src
. If any of those ui/apos
located files use the Modules/
alias, it will not be resolved correctly and result in a build error. Note that a common example of this is importing SASS files.
The Modules/
Alias
The Modules/
alias is available for both public and admin UI builds. It allows you to import modules without worrying about relative paths, but restricts you to sources inside either the ui/src/
or ui/apos
directories, respectively, depending on the build.
// Current file: modules/another-module/ui/src/index.js
// Actual import path: modules/some-module/ui/src/lib/utils.js
import utils from 'Modules/some-module/lib/utils.js';
Important Considerations
When using the @/
alias:
- You can access
public
builds from within theapos
build, and vice versa - Use with caution as it might lead to import resolution issues if:
- The imported file contains
Modules/
aliased imports - Deep imports within the imported file contain
Modules/
aliased imports
- The imported file contains
- Benefits of using
@/
:- More developer-friendly
- Enables auto-completion in supported editors
- More intuitive and readable paths
- Best practices:
- Include mostly sources from your current build
- Ensure imported sources don't contain
Modules/
aliased imports when cross-importing
Resolving Alias Errors
The error message 'Unable to resolve module source "X/X/X/X.js" from alias "X/X/X.js"', typically occurs when the Modules/
alias cannot correctly resolve the source file you're trying to import. This can happen for several reasons:
- The target file doesn't exist in the expected location
- You're trying to import a file from
ui/apos
using theModules/
alias (which only works withui/src
files) - The import path doesn't follow the expected structure for the
Modules/
alias
The Modules/ alias resolves paths using this pattern:
Modules/[module-name]/[path-within-src]
Where:
[module-name]
is the name of your module[path-within-src]
is the path to your file, relative to that module'sui/src
directory
If you're seeing this error, consider:
- Double-checking the file path and ensuring the file exists
- Using the
@/
alias instead, especially if you need to access files outside ofui/src
- Reviewing the module name and path structure in your import statement
Editor Configuration
To enable proper path resolution and autocompletion in your code editor, add a jsconfig.json
file to your project root:
{
"compilerOptions": {
"baseUrl": "./apos-build/@apostrophecms/vite/default",
"paths": {
"@/*": ["./src/*"]
}
}
}
Note: If you've changed your project asset namespace from the default, adjust the baseUrl
and exclude
paths accordingly. For example, with a namespace of my-namespace
:
baseUrl
should be./apos-build/@apostrophecms/vite/my-namespace
exclude
should includeapos-build/@apostrophecms/vite/my-namespace/dist
WARNING
When following imports in your editor (e.g., Ctrl + Click in VSCode), you'll be taken to the apos-build
directory rather than the original source code. This is because the apos-build
directory contains a complete copy of your project's source code (including Admin UI) from all modules (local and npm) and serves as Vite's actual source directory for building the project. Do not modify code in this folder as your changes will be lost. Change files in the original folder.
Asset Handling
The Vite integration allows direct imports of static assets like images, fonts, and other files, as well as Sass files, in both public and admin UI builds.
Importing Static Assets
You can import assets directly in your JavaScript/framework code:
// Using aliases or relative paths
// Actual path: modules/some-module/ui/assets/logo.svg
import logo from '@/some-module/assets/logo.svg';
// The logo variable now contains the normalized path to the image
Framework-Specific Asset Imports
Vue
<template>
<!-- Direct template imports -->
<img src="@/some-module/assets/logo.svg" alt="My logo" />
</template>
React
import logo from '@/some-module/assets/logo.svg';
function MyComponent() {
return <img src={logo} alt="My logo" />;
}
Importing Sass
You can import Sass files using standard import syntax:
/* Using aliases or relative paths */
/* Actual path: modules/some-module/ui/scss/_styles.scss */
@use '@/some-module/scss/styles';
CSS URL Resolution
There are two ways to resolve URLs in your CSS:
- Using the Public Folder
/* File location: ./modules/some-module/public/font.ttf */
@font-face {
font-family: MyFont;
src: url("/modules/some-module/font.ttf") format("truetype");
}
- Using Source Root Path
/* File location: ./modules/some-module/ui/fonts/font.ttf */
@font-face {
font-family: Inter;
src: url("/src/some-module/fonts/font.ttf") format("truetype");
}
TIP
You can inspect the sources in the apos-build/@apostrophecms/vite/default
directory to understand how Vite resolves these paths when building your project.
Extending Vite Configuration
ApostropheCMS's Vite integration provides two methods to customize your Vite configuration. You can either configure it through your module's code or via a dedicated configuration file in your project root.
Method 1: Module Configuration
Use the build.vite
property in your module to extend Vite's configuration:
Method 2: Project Configuration File
For project-wide Vite settings, create either apos.vite.config.js (if your project uses ESM with "type": "module" in package.json) or apos.vite.config.mjs (if your project uses CommonJS) in your project root. The configuration code inside the file should always use ESM syntax regardless of your project type:
Common Configuration Options
Here are some frequently used Vite configuration options:
- define: Replace constants in your code during build time
- plugins: Add Vite plugins for additional functionality
- css: Configure CSS processing and preprocessors
- build: Customize build output and bundling behavior
- optimizeDeps: Control dependency pre-bundling
In most cases, any server options shouldn't be touched in the Vite config. For a complete list of available options, refer to the Vite Configuration Documentation.
Development-Specific Features
Conditional Code Injection
When building UI components, you may want certain elements to appear only during development or when HMR is active. ApostropheCMS's Nunjucks components system makes this easy to manage.
Component Setup
First, create your component template:
Then, register and configure the component in your module:
Visibility Options
The when option controls when your component appears:
when: 'hmr' // Only visible when HMR is active
when: 'dev' // Visible in any development mode
when: 'prod' // Only visible in production
The bundler option allows you to specify which bundler must be active for the component to appear:
bundler: 'vite' // Only visible when using Vite
bundler: 'webpack' // Only visible when using webpack
You can combine these options to precisely control when your component appears. For example, to show a component only when using Vite with HMR active, you would use both when: 'hmr'
and bundler: 'vite'
.
Common Use Cases
- Development toolbars
- Debug information
- Performance monitoring
- Asset reload buttons
WARNING
Remember that components marked with when: 'dev'
or when: 'hmr'
will never appear in production, regardless of other settings.
Migration and Technical Considerations
Moving from Webpack to Vite
When migrating your ApostropheCMS project to use Vite, you'll need to make a few code adjustments:
Update CSS/Sass Imports
scss// Old webpack style @import "~normalize.css"; // New Vite style - remove the ~ prefix @import "normalize.css";
Update Module Import Paths
javascript// Not recommended import Component from 'apostrophe/modules/module-name/ui/apos/components/Component.vue'; // Recommended - use the alias import Component from 'Modules/module-name/components/Component.vue';
Convert to ESM Syntax
javascript// Remove CommonJS syntax const myComponent = require('./component'); module.exports = myComponent; // Use ESM instead import myComponent from './component.js'; // Note: File extension required export default myComponent; // When importing your own modules, always include the file extension import { helper } from './utils.js'; import styles from './styles.css'; import template from './template.html';
INFO
ESM requires file extensions in import paths. Always include .js
, .css
, .vue
, etc. when importing your own modules. This is different from webpack, which allowed omitting extensions.
Known Limitations and Solutions
HMR Limitations
New UI Directories
- Issue: HMR only watches existing
anyModule/ui
directories - Solution: After adding new
ui
directories, restart your development server:bash# Using nodemon (default setup) rs
- Issue: HMR only watches existing
Vue and Admin UI
- Issue: Admin UI HMR (
hmr: 'apos'
) won't work when the public build contains Vue sources - Solution: Use separate pages for Vue development, or stick to
hmr: 'public'
when working with Vue components - Note: Public build HMR continues to work as expected
- Issue: Admin UI HMR (
Public Assets
- Issue: Changes to
ui/public
directories don't trigger HMR or page reloads - Solution: Add the directories to your nodemon watch list in
package.json
:json{ "nodemonConfig": { "watch": [ "./app.js", "./modules/**/*", "./lib/**/*.js", "./views/**/*.html", "./modules/*/ui/public/**/*" ] } }
Build and Performance
Source Map Issues
- Issue: Source maps not working correctly in development
- Solutions:
- Clear your browser's DevTools cache
- Disable source-map-related browser extensions
- Verify file paths in your imports are correct
Build Errors
- If you encounter build errors, try clearing your build cache:bash
node app @apostrophecms/asset:reset npm run build
- If you encounter build errors, try clearing your build cache:
Common Workarounds
- For most issues, try these steps in order:
- Clear your browser cache
- Reset the asset build
- Restart your development server
- Check the browser console for specific error messages
TIP
Remember to check the terminal output and browser console for specific error messages. Most HMR issues will show clear error messages indicating the problem.