Tree-Shaking & Performance
How tree-shaking works in @acronis-platform/ui-react and best practices for optimal bundle size.
Tree-Shaking & Performance
Overview
@acronis-platform/ui-react is built to be tree-shakeable, so your
production bundle includes only the components you actually import.
Tree-Shaking Enabled
The library is built with preserveModules: true in its Vite library config
(packages/ui-react/vite.lib.config.ts), which preserves the original module
structure. This lets modern bundlers (Vite, Webpack 5+, Rollup) drop the
components you don't use.
What This Means
// You import only what you need
import { Button } from '@acronis-platform/ui-react';
// Your production bundle includes:
// the Button component + its dependencies (cn utility, cva, the Base UI
// primitives it wraps)
// NOT included: Switch, InputText, Tag, or any other unused componentBuild Output Structure
The library is built with preserved module structure — each component compiles
to its own file under dist/:
dist/
├── index.js (re-exports all components)
├── react.js (the ./react subpath entry)
├── components/
│ └── ui/
│ ├── button/button.js (individual component)
│ ├── switch/switch.js (individual component)
│ ├── input-text/input-text.js
│ ├── tag/tag.js
│ └── ... (24 component modules, each separate)
├── lib/
│ └── utils.js (cn class-merge helper)
└── ui-react.css (compiled styles, imported separately)How Tree-Shaking Works
1. ES Module Format
The library is built as ES modules (formats: ['es']), which is required for
tree-shaking:
// Tree-shakeable (ES modules)
export { Button } from './components/ui/button';
export { Switch } from './components/ui/switch';
// Not tree-shakeable (CommonJS)
module.exports = { Button, Switch };ui-react ships ESM only — there is no CommonJS entry.
2. Preserved Module Structure
Each component is a separate file, so bundlers can analyze dependencies precisely:
// When you import Button
import { Button } from '@acronis-platform/ui-react';
// Bundler traces dependencies:
// index.js -> components/ui/button/button.js -> lib/utils.js
// Only these files are included in your bundle3. Peer Dependencies Stay External
react, react-dom, @base-ui/react, and @acronis-platform/icons-react are
marked external in the build, so they are not inlined into the bundle.
Consumers install them once, and bundlers de-duplicate them across your app and
the library.
Bundler Compatibility
Vite (Recommended)
Vite has excellent tree-shaking by default:
// vite.config.ts
export default {
build: {
rollupOptions: {
output: {
manualChunks: {
// Optional: separate vendor chunk
'ui-components': ['@acronis-platform/ui-react'],
},
},
},
},
};Webpack 5+
Webpack 5 supports tree-shaking with production mode:
// webpack.config.js
module.exports = {
mode: 'production', // Enables tree-shaking
optimization: {
usedExports: true,
sideEffects: true,
},
};Next.js
Next.js automatically tree-shakes in production:
// No configuration needed
import { Button } from '@acronis-platform/ui-react';Rollup
Rollup has built-in tree-shaking:
// rollup.config.js
export default {
output: {
format: 'es',
},
};Optimization Tips
1. Import from the Main Entry
Always import from the package entry:
// Recommended — tree-shakeable
import { Button, Switch } from '@acronis-platform/ui-react';
// Avoid — reaches into build internals, which may change
import { Button } from '@acronis-platform/ui-react/dist/components/ui/button/button';2. Use Named Imports
Use named imports, not namespace imports:
// Good — tree-shakeable
import { Button, Switch } from '@acronis-platform/ui-react';
// Avoid — may pull in the entire module
import * as UI from '@acronis-platform/ui-react';3. Import Styles Once
CSS is not tree-shakeable. ui-react ships a single compiled stylesheet;
import it once at your app entry:
import '@acronis-platform/ui-react/styles';4. Lazy Load Heavy Composites
For larger composite components you don't need on first paint (e.g. the
sidebars or Resizable), consider lazy loading:
import { lazy } from 'react';
const SidebarSecondary = lazy(() =>
import('@acronis-platform/ui-react').then((mod) => ({
default: mod.SidebarSecondary,
}))
);Bundle Analysis
Analyze Your Bundle
Use bundle analyzers to verify tree-shaking:
Vite:
npm install -D rollup-plugin-visualizer// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer';
export default {
plugins: [visualizer()],
};Webpack:
npm install -D webpack-bundle-analyzer// webpack.config.js
const BundleAnalyzerPlugin =
require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [new BundleAnalyzerPlugin()],
};Expected Results
After analysis, you should see:
- Only the components you import in your bundle
- Peer dependencies (React,
@base-ui/react) shared, not duplicated - Unused components excluded
Performance Best Practices
1. Code Splitting by Route
Split components by route for optimal loading:
// Route-based splitting
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));2. Dynamic Imports
Load components on demand:
// Load a component when needed
const loadResizable = async () => {
const { Resizable } = await import('@acronis-platform/ui-react');
return Resizable;
};3. Preload Critical Components
Import the components needed for first render up front:
// Statically imported, available immediately
import { Button, InputText, Switch } from '@acronis-platform/ui-react';Common Issues
Issue: Bundle includes all components
Cause: Using import * or importing from the wrong path.
Solution:
// Wrong
import * as UI from '@acronis-platform/ui-react';
// Correct
import { Button, Switch } from '@acronis-platform/ui-react';Issue: Tree-shaking not working
Cause: Development mode or a misconfigured bundler.
Solution:
- Ensure production mode:
NODE_ENV=production - Check your bundler configuration
- Confirm you import from the package entry, not
dist/internals
Issue: Large bundle size
Cause: Importing heavy dependencies.
Solution:
- Use a bundle analyzer to identify large modules
- Lazy load heavy composites
- Consider code splitting
Summary
@acronis-platform/ui-react is built for production:
- Tree-shakeable — preserved module structure lets bundlers drop unused components
- ESM-only — modern, statically analyzable output
- External peers — React and
@base-ui/reactare not bundled in - Modern bundlers — works with Vite, Webpack 5+, Next.js, and Rollup
For most apps, importing a handful of components results in a small footprint relative to the full library. Verify the exact savings for your app with a bundle analyzer.
Edit on GitHub