Using Rollup's preserveModules to Keep ES Module Structure
Learn how Rollup's preserveModules option keeps your ES module file structure intact while allowing tree‑shaking of external dependencies.
11 Feb 2026, 11:06 UTC

What preserveModules does
When preserveModules is set to true, Rollup emits one output file for each input module instead of bundling everything into a single file. The original directory tree is reproduced under the output directory, and external dependencies remain as ES‑module imports so consumers can tree‑shake unused exports.
How to configure it
The feature only works with the ES module output format (format: 'es'). You can optionally set preserveModulesRoot to control which part of the source tree is kept; if omitted, the project root is used.
Example configuration
// rollup.config.js
export default {
input: 'src/index.js',
output: {
dir: 'dist',
format: 'es',
preserveModules: true,
// keep the src folder structure inside dist
preserveModulesRoot: 'src'
}
};
Run the build from the project root (you need read/write access to the project directory):
npx rollup -c
Limits and common mistakes
- Format restriction: Using
preserveModuleswithformat: 'cjs'orformat: 'umd'throws an error. - Incorrect root: If
preserveModulesRootpoints to a directory that does not contain the input files, the output will be flattened, breaking relative import paths. - Path‑rewriting plugins: Plugins such as
@rollup/plugin-aliasor@rollup/plugin-replacemust be configured to respect the preserved module structure; otherwise imports may point to non‑existent files. - Code‑splitting interaction: Manual dynamic imports (
import()) can cause duplicate files when combined withpreserveModules. Test the bundle in a consumer project to verify there are no unexpected duplicates.
How to verify the result
- After running
npx rollup -c, inspect thedistfolder. It should mirror thesrcfolder with a.jsfile for each module. - Open a generated file and confirm that external dependencies appear as ES‑module imports (e.g.,
import { foo } from 'lodash') rather than being inlined. - Create a test project that imports a specific function from the bundle (e.g.,
import { useful } from './dist/src/myModule.js'). Run a bundler or check the bundle size; unused exports from the same module should not increase the final bundle size.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.