--- name: performance description: MUI performance optimization — tree-shaking, bundle size, rendering, SSR triggers: - performance - bundle size - tree-shaking - optimization - lazy loading - SSR - Next.js allowed-tools: - Read - Glob - Grep - Bash - Write - Edit globs: - "*.tsx" - "*.ts" - "*.jsx" - "*.js" - "webpack.config.*" - "next.config.*" --- # MUI Performance Optimization ## Tree-Shaking — Named Imports Only Use named imports from `@mui/material`. Never import from barrel files or index — bundlers cannot tree-shake those effectively. ```tsx // BAD — imports the entire @mui/material bundle (~300 KB+ gzipped) import { Button, TextField, Dialog } from '@mui/material'; // GOOD — each import is individually tree-shaken import Button from '@mui/material/Button'; import TextField from '@mui/material/TextField'; import Dialog from '@mui/material/Dialog'; ``` ### Icons — always deep import ```tsx // BAD — imports all 2100+ icons (~1 MB+) import { Delete, Edit, Add } from '@mui/icons-material'; // GOOD — only the used icon is bundled import DeleteIcon from '@mui/icons-material/Delete'; import EditIcon from '@mui/icons-material/Edit'; import AddIcon from '@mui/icons-material/Add'; ``` ### babel-plugin-import (alternative for barrel imports) If you must use named imports from barrels, configure the plugin to transform them: ```json // .babelrc { "plugins": [ ["babel-plugin-import", { "libraryName": "@mui/material", "libraryDirectory": "", "camel2DashComponentName": false }] ] } ``` ## Bundle Analysis ```bash # Install source-map-explorer npm install --save-dev source-map-explorer # Add to package.json "scripts": { "analyze": "source-map-explorer 'build/static/js/*.js'" } # For Next.js, use @next/bundle-analyzer npm install --save-dev @next/bundle-analyzer ``` ```js // next.config.js const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true', }); module.exports = withBundleAnalyzer({}); // Run: // ANALYZE=true npm run build ``` ```bash # Webpack bundle analyzer (CRA or custom webpack) npm install --save-dev webpack-bundle-analyzer # In webpack config: const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); plugins: [new BundleAnalyzerPlugin()] ``` ## Emotion Caching Without caching, Emotion regenerates style sheets on every SSR request. Use `createCache` with a `CacheProvider` for significant SSR performance gains. ```tsx // lib/createEmotionCache.ts import createCache from '@emotion/cache'; export default function createEmotionCache() { return createCache({ key: 'css', prepend: true }); } ``` ```tsx // _app.tsx (Next.js Pages Router) import { CacheProvider, EmotionCache } from '@emotion/react'; import { ThemeProvider } from '@mui/material/styles'; import CssBaseline from '@mui/material/CssBaseline'; import createEmotionCache from '../lib/createEmotionCache'; const clientSideEmotionCache = createEmotionCache(); interface MyAppProps extends AppProps { emotionCache?: EmotionCache; } export default function MyApp({ Component, emotionCache = clientSideEmotionCache, pageProps }: MyAppProps) { return ( ); } ``` ```tsx // _document.tsx — inject emotion styles before MUI styles import Document, { Html, Head, Main, NextScript } from 'next/document'; import createEmotionServer from '@emotion/server/create-instance'; import createEmotionCache from '../lib/createEmotionCache'; export default class MyDocument extends Document { render() { return ( {(this.props as any).emotionStyleTags}
); } } MyDocument.getInitialProps = async (ctx) => { const cache = createEmotionCache(); const { extractCriticalToChunks } = createEmotionServer(cache); const originalRenderPage = ctx.renderPage; ctx.renderPage = () => originalRenderPage({ enhanceApp: (App: any) => (props) => , }); const initialProps = await Document.getInitialProps(ctx); const emotionStyles = extractCriticalToChunks(initialProps.html); const emotionStyleTags = emotionStyles.styles.map((style) => (