Optimize application bundle size by importing only necessary components and using tree-shaking effectively
Use this skill to reduce the bundle size of an application that uses Ignite UI Web Components. Import only the necessary components. Follow the best practices for tree-shaking.
defineAllComponents()igc-theme-<name> condition if the app uses only one theme// DON'T DO THIS - imports ALL components (~500KB+)
import { defineAllComponents } from 'igniteui-webcomponents';
defineAllComponents();
Impact: The bundle includes all 60+ components, also the components that you do not use.
// DO THIS - import only what you need
import {
defineComponents,
IgcButtonComponent,
IgcInputComponent,
IgcCardComponent
} from 'igniteui-webcomponents';
defineComponents(IgcButtonComponent, IgcInputComponent, IgcCardComponent);
Impact: The bundle includes only 3 components and their dependencies.
If you use React, you can use the igniteui-react package instead of igniteui-webcomponents. It has the same components with React-friendly wrappers. It usually gives better tree-shaking:
npm install igniteui-react
import { IgrButton, IgrInput, IgrCard } from 'igniteui-react';
// No need to call defineComponents - components register automatically
function MyComponent() {
return (
<div>
<IgrButton variant="contained">Click me</IgrButton>
<IgrInput label="Name" />
<IgrCard>Content</IgrCard>
</div>
);
}
Benefits for bundle size:
For more details, see the igniteui-wc-integrate-with-framework skill.
Use a bundle analyzer to identify the contents of the bundle before and after optimization.
Vite projects — rollup-plugin-visualizer:
npm install --save-dev rollup-plugin-visualizer
Add visualizer() to the Vite plugins array. Then run npm run build. The plugin opens a treemap in the browser.
Webpack projects — webpack-bundle-analyzer:
npm install --save-dev webpack-bundle-analyzer
Add BundleAnalyzerPlugin to the plugins in webpack.config.js. Then run npm run build.
Framework-agnostic — source-map-explorer:
npm install --save-dev source-map-explorer
# Then: source-map-explorer 'dist/**/*.js'
Search your codebase for component usage:
# Search for component tags in templates
grep -r "igc-" src/ --include="*.html" --include="*.tsx" --include="*.vue"
# List unique components
grep -roh "igc-[a-z-]*" src/ | sort | uniq
Compare the components that you import with the components that you use:
// Find in your code
import {
defineComponents,
IgcButtonComponent,
IgcInputComponent,
IgcCardComponent,
IgcSelectComponent, // ← Are you using this?
IgcComboComponent, // ← Are you using this?
} from 'igniteui-webcomponents';
Remove the components that you do not use:
// Before: 5 components imported
import {
defineComponents,
IgcButtonComponent,
IgcInputComponent,
IgcCardComponent,
IgcSelectComponent,
IgcComboComponent,
} from 'igniteui-webcomponents';
defineComponents(
IgcButtonComponent,
IgcInputComponent,
IgcCardComponent,
IgcSelectComponent,
IgcComboComponent
);
// After: Only 3 components needed
import {
defineComponents,
IgcButtonComponent,
IgcInputComponent,
IgcCardComponent,
} from 'igniteui-webcomponents';
defineComponents(IgcButtonComponent, IgcInputComponent, IgcCardComponent);
To make the initial bundle smaller, load components only when the app needs them.
// Load immediately (increases initial bundle)
import { defineComponents, IgcDialogComponent } from 'igniteui-webcomponents';
defineComponents(IgcDialogComponent);
// Lazy load (smaller initial bundle)
async function showDialog() {
const { defineComponents, IgcDialogComponent } = await import('igniteui-webcomponents');
defineComponents(IgcDialogComponent);
const dialog = document.createElement('igc-dialog');
// ... use dialog
}
import React, { lazy, Suspense, useState } from 'react';
// Lazy load the dialog component
const IgrDialog = lazy(() =>
import('igniteui-react').then(module => ({ default: module.IgrDialog }))
);
function MyComponent() {
const [showDialog, setShowDialog] = useState(false);
return (
<>
<button onClick={() => setShowDialog(true)}>Open Dialog</button>
{showDialog && (
<Suspense fallback={<div>Loading...</div>}>
<IgrDialog open>
<h2>Dialog Content</h2>
</IgrDialog>
</Suspense>
)}
</>
);
}
import React, { useState } from 'react';
// Lazy load component registration
const lazyLoadDialog = async () => {
const { defineComponents, IgcDialogComponent } = await import('igniteui-webcomponents');
defineComponents(IgcDialogComponent);
};
function MyComponent() {
const [dialogReady, setDialogReady] = useState(false);
const openDialog = async () => {
if (!dialogReady) {
await lazyLoadDialog();
setDialogReady(true);
}
// Show dialog
};
return (
<button onClick={openDialog}>Open Dialog</button>
);
}
<script setup lang="ts">
import { ref } from 'vue';
const dialogReady = ref(false);
async function openDialog() {
if (!dialogReady.value) {
const { defineComponents, IgcDialogComponent } = await import('igniteui-webcomponents');
defineComponents(IgcDialogComponent);
dialogReady.value = true;
}
// Show dialog
}
</script>
<template>
<button @click="openDialog">Open Dialog</button>
<igc-dialog v-if="dialogReady" open>
<h2>Dialog Content</h2>
</igc-dialog>
</template>
import { Component } from '@angular/core';
@Component({
selector: 'app-my-component',
template: `
<button (click)="openDialog()">Open Dialog</button>
<igc-dialog *ngIf="dialogReady" [open]="true">
<h2>Dialog Content</h2>
</igc-dialog>
`
})
export class MyComponent {
dialogReady = false;
async openDialog() {
if (!this.dialogReady) {
const { defineComponents, IgcDialogComponent } = await import('igniteui-webcomponents');
defineComponents(IgcDialogComponent);
this.dialogReady = true;
}
}
}
Load Ignite UI components only for the routes that need them. To do this, put the defineComponents(...) calls in the lazy-loaded route module for each framework.
Put the component imports and defineComponents at the top of each page module. React lazy() + Suspense does the async split:
// pages/Dashboard.tsx
import { IgrCard, IgrButton } from 'igniteui-react';
function Dashboard() {
return (
<div>
<IgrCard>
<h2>Dashboard</h2>
<p>Dashboard content here</p>
</IgrCard>
</div>
);
}
To put pages/Dashboard in a separate chunk, refer to the lazy-loading documentation of your router (React Router, TanStack Router, etc.).
Use onMounted to register components only when the route mounts:
<script setup lang="ts">
import { onMounted } from 'vue';
onMounted(async () => {
// Load components only for this route
const { defineComponents, IgcCardComponent } = await import('igniteui-webcomponents');
defineComponents(IgcCardComponent);
});
</script>
Refer to the Vue Router documentation for the () => import('./views/Dashboard.vue') lazy-route syntax.
Put defineComponents(...) in the module or component of the route. Then only that lazy chunk includes it. Refer to the Angular Router documentation for loadChildren / loadComponent lazy loading.
Make sure that your build tool runs in production mode with minification enabled. For Vite, vite build does this by default. For Webpack, set mode: 'production'.
To reduce chunk-size warnings from Ignite UI components, increase the chunkSizeWarningLimit in Vite. In Webpack, configure splitChunks to put the igniteui-* packages in a named vendor chunk. For the exact configuration options, refer to the documentation of your build tool.
Each component ships the styles of all four themes (Material, Bootstrap, Indigo and Fluent) and applies the active one. If the app uses only one theme, set the igc-theme-<name> condition in the bundler. The bundle then contains only the component styles of that theme, light and dark, and is approximately 15–17% smaller after gzip.
For example, in Vite:
// vite.config.ts
import { defaultClientConditions, defineConfig } from 'vite';
export default defineConfig({
resolve: { conditions: ['igc-theme-bootstrap', ...defaultClientConditions] },
});
| Tool | Setting |
|---|---|
| Vite | resolve: { conditions: ['igc-theme-bootstrap', ...defaultClientConditions] }, with defaultClientConditions imported from vite. For SSR, also set ssr.resolve.conditions with defaultServerConditions. |
| webpack | resolve: { conditionNames: ['igc-theme-bootstrap', '...'] } |
| esbuild | conditions: ['igc-theme-bootstrap'] |
| Rollup | nodeResolve({ exportConditions: ['igc-theme-bootstrap'] }) |
| Node.js (SSR) | node --conditions=igc-theme-bootstrap server.js |
igc-theme-material, igc-theme-bootstrap, igc-theme-indigo and igc-theme-fluent. Set only one.configureTheme() with another theme. A theme that is not in the bundle gives only the structural styles.igniteui-webcomponents/themes/light/bootstrap.css.igniteui-react uses igniteui-webcomponents, so the same condition applies.The actual sizes depend mostly on the components that you import. Grid and chart components are much larger than UI components. Use your bundle analyzer to measure the real effect in your project. Do not rely on generic estimates.
The key rule: import a subset of components with defineComponents(), not all components with defineAllComponents(). The bundle then decreases by the size of each component that you do not include. It also decreases by the size of all exclusive dependencies of these components.
defineAllComponents() if the app does not need every componentdefineComponents() with the specific components that you needigc-theme-<name> condition if the app uses only one themeInvestigate:
igniteui-react instead of igniteui-webcomponentsSolutions:
// Review your imports - are you using all of these?
import {
defineComponents,
IgcButtonComponent,
IgcInputComponent,
IgcSelectComponent,
IgcComboComponent,
IgcDatePickerComponent
} from 'igniteui-webcomponents';
// Consider lazy loading components you don't need immediately
async function loadDialog() {
const { defineComponents, IgcDialogComponent } = await import('igniteui-webcomponents');
defineComponents(IgcDialogComponent);
}
Cause: You did not import a component that you use.
Solution:
// Error: <igc-button> not working
// You're using igc-button but didn't import it
import { defineComponents, IgcButtonComponent } from 'igniteui-webcomponents';
defineComponents(IgcButtonComponent); // Add this
Solution: Update the type imports:
// Import types separately if needed
import type { IgcButtonComponent } from 'igniteui-webcomponents';
To monitor the bundle size in CI continuously, you can use tools such as bundlesize, size-limit, or the Bundlewatch GitHub Action. These tools can fail a pull request if the bundle is larger than a defined limit. After you complete the import optimization, configure a size limit that is applicable to the component set of your project.