Vite Build Configuration#
Overview#
The Strapi admin panel uses Vite as its bundler. The central configuration lives in packages/core/strapi/src/node/vite/config.ts and exports three factory functions :
resolveBaseConfig— shared base applied to all modesresolveProductionConfig— adds minification, sourcemaps, and a single Rollup entry pointresolveDevelopmentConfig— adds HMR, middleware mode, and monorepo dev aliases
User overrides go in src/admin/vite.config.{js,mjs,ts,mts} as a transform function (config) => config merged with Vite's mergeConfig .
The Three-Layer Deduplication System#
The core problem this config solves: dynamically-loaded plugin chunks may resolve a different physical copy of a shared package than the main admin bundle. When a package keeps state in module scope (React context, Redux store, DnD context, CodeMirror extension registry), two copies means two independent instances that can't communicate — causing crashes, blank screens, or instanceof failures.
Three Vite mechanisms are layered together to guarantee a single runtime instance:
| Layer | Vite Option | Effect |
|---|---|---|
| 1 | optimizeDeps.include | Force-converts and caches a single pre-bundled ESM copy |
| 2 | resolve.dedupe | Tells Rollup to always resolve to the same package directory |
| 3 | resolve.alias | Hard-maps the specifier to a specific filesystem path (critical for pnpm) |
Controlled by ADMIN_VITE_ALIAS_MODULES#
The canonical list of packages that receive all three treatments is defined in admin-vite-alias-modules.ts :
- React core:
react,react-dom,react-router-dom,styled-components - Redux stack:
react-redux,@reduxjs/toolkit - DnD:
react-dnd,react-dnd-html5-backend - Design system:
@strapi/design-system,@radix-ui/react-tooltip - Utilities:
lodash,invariant,prismjs
Packages only direct-declared in @strapi/admin (not re-exported via the exports map) live in a separate ADMIN_PINNED_ALIAS_MODULES subset .
CodeMirror Singletons (ADMIN_VITE_SINGLETON_MODULES)#
CodeMirror v6 packages — @codemirror/state, @codemirror/view, @codemirror/language, @codemirror/lang-json, @codemirror/commands, @codemirror/lint, @uiw/react-codemirror — are managed in a separate file admin-vite-singleton-modules.ts. These use resolve.dedupe and resolve.alias (resolved from @strapi/design-system's closure) but are not in ADMIN_VITE_ALIAS_MODULES because they are not direct @strapi/admin dependencies.
Dedupe-Only: @strapi/strapi#
@strapi/strapi is added to resolve.dedupe but deliberately not aliased. Aliasing packages that use exports maps causes [vite:load-fallback] errors because resolve.alias bypasses the package exports map. Adding @strapi/strapi to dedupe alone collapses ~98% of module duplication from local plugins .
Alias Resolution: Scoped to @strapi/admin#
All aliases are built by buildAdminViteResolveAliases() using getModulePath(mod), which resolves each package path starting from @strapi/admin's package directory (not @strapi/strapi's), preventing pnpm's strict isolation from picking up a hoisted incompatible version .
Auto-Exclude Heuristic for Plugin UI Libs#
PR #26944 introduced admin-vite-optimize-exclude.ts, which automatically adds pre-built ESM plugin UI libraries to optimizeDeps.exclude . A package is auto-excluded if all three conditions hold:
react/react-domin itspeerDependencies- ESM (
type: moduleorexports.import) - Pre-built dist output (
dist/entry orfiles: ['dist'])
Safety guards: official @strapi/* packages are never auto-excluded, and packages on ADMIN_VITE_ALIAS_MODULES are always kept on the include path .
Notable Regressions & Fixes#
| Package | Symptom | PR |
|---|---|---|
react-redux, @reduxjs/toolkit | "could not find react-redux context value" after upgrades / pnpm monorepo | #26249, #26756 |
prismjs | Blank admin / ReferenceError: Prism is not defined (5.49–5.50.1) | #26978, #27086 |
@codemirror/* | "multiple instances of @codemirror/state" — JSON field crash | #27007 |
react-dnd | "Invariant Violation: Expected drag drop context" — Content Manager crash | #27217 |
react-query | "No QueryClient set" — Media Library crash | #27266 |
@strapi/strapi auto-excluded | Blank admin from missing invariant CJS conversion | #27014 |
Key Source Files#
| File | Purpose |
|---|---|
node/vite/config.ts | Main Vite config factory (resolveBaseConfig, resolveProductionConfig, resolveDevelopmentConfig) |
node/core/admin-vite-alias-modules.ts | Canonical alias/dedupe lists (ADMIN_VITE_ALIAS_MODULES, ADMIN_VITE_DEDUPE_MODULES, ADMIN_PINNED_ALIAS_MODULES) |
node/core/admin-vite-singleton-modules.ts | CodeMirror singleton list (ADMIN_VITE_SINGLETON_MODULES) |
node/core/admin-vite-optimize-exclude.ts | Auto-exclude heuristic for plugin UI libs |
node/core/resolve-module.ts | getModulePath() — resolves aliases from @strapi/admin's closure |
node/core/aliases.ts | getMonorepoAliases() — dev-only src aliases for the Strapi source monorepo |
node/core/linked-packages.ts | isPackageLinked() — detects portal:/file: symlinks to adjust optimizeDeps |
Extending the Config#
Add a src/admin/vite.config.ts exporting a transform function:
import { mergeConfig, type UserConfig } from 'vite';
export default (config: UserConfig) => mergeConfig(config, {
resolve: {
dedupe: ['my-singleton'],
},
});
See Strapi docs — admin panel customization for the full pattern .