Skip to content
intermediatePhase 38 · Web Performance

Code Splitting

Split bundles with dynamic imports for faster initial page loads.

45m
0 problems
Topic Progress0%

Dynamic Imports

Dynamic Imports

Load JavaScript modules on demand instead of upfront.

Basic Dynamic Import

// Static import (loaded upfront)
import { heavyFunction } from './utils';

// Dynamic import (loaded on demand)
const module = await import('./utils');
module.heavyFunction();

React.lazy

import { lazy, Suspense } from 'react';

// Dynamic component import
const HeavyComponent = lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<Spinner />}>
      <HeavyComponent />
    </Suspense>
  );
}

Named Exports

// Component with named exports
// HeavyComponent.js
export function ComponentA() { ... }
export function ComponentB() { ... }

// Dynamic import with named exports
const { ComponentA } = await import('./HeavyComponent');

// React.lazy with named export
const ComponentA = lazy(() =>
  import('./HeavyComponent').then(module => ({
    default: module.ComponentA,
  }))
);

Conditional Loading

function App({ showAdvanced }) {
  const [AdvancedComponent, setAdvancedComponent] = useState(null);

  useEffect(() => {
    if (showAdvanced && !AdvancedComponent) {
      import('./AdvancedComponent').then(module => {
        setAdvancedComponent(() => module.default);
      });
    }
  }, [showAdvanced, AdvancedComponent]);

  return (
    <div>
      <BasicComponent />
      {AdvancedComponent && <AdvancedComponent />}
    </div>
  );
}

Webpack Magic Comments

// Prefetch during idle time
const module = import(
  /* webpackChunkName: "dashboard" */
  /* webpackPrefetch: true */
  './Dashboard'
);

// Preload during current load
const module = import(
  /* webpackChunkName: "settings" */
  /* webpackPreload: true */
  './Settings'
);

Route-Based Splitting

Route-Based Splitting

Split code based on routes for optimal loading.

React Router Implementation

import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

// Lazy load routes
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));

// Loading component
const PageLoader = () => (
  <div className="page-loader">
    <Spinner />
    <p>Loading page...</p>
  </div>
);

function App() {
  return (
    <BrowserRouter>
      <Layout>
        <Suspense fallback={<PageLoader />}>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/about" element={<About />} />
            <Route path="/dashboard" element={<Dashboard />} />
            <Route path="/settings" element={<Settings />} />
          </Routes>
        </Suspense>
      </Layout>
    </BrowserRouter>
  );
}

Prefetching Routes

import { Link } from 'react-router-dom';

function NavLink({ to, children }) {
  const prefetch = () => {
    // Prefetch route component
    import(`./pages/${to.slice(1) || 'Home'}`);
  };

  return (
    <Link to={to} onMouseEnter={prefetch}>
      {children}
    </Link>
  );
}

Layout Groups

// Shared layout for auth pages
const AuthLayout = lazy(() => import('./layouts/AuthLayout'));
const Login = lazy(() => import('./pages/auth/Login'));
const Register = lazy(() => import('./pages/auth/Register'));

// Shared layout for dashboard
const DashboardLayout = lazy(() => import('./layouts/DashboardLayout'));
const Overview = lazy(() => import('./pages/dashboard/Overview'));
const Analytics = lazy(() => import('./pages/dashboard/Analytics'));

function App() {
  return (
    <Suspense fallback={<PageLoader />}>
      <Routes>
        <Route element={<AuthLayout />}>
          <Route path="/login" element={<Login />} />
          <Route path="/register" element={<Register />} />
        </Route>
        <Route element={<DashboardLayout />}>
          <Route path="/dashboard" element={<Overview />} />
          <Route path="/dashboard/analytics" element={<Analytics />} />
        </Route>
      </Routes>
    </Suspense>
  );
}

Component Splitting

Component Splitting

Split individual components for on-demand loading.

Heavy Component Loading

import { useState, lazy, Suspense } from 'react';

// Chart library loaded only when needed
const Chart = lazy(() => import('./Chart'));
const DataGrid = lazy(() => import('./DataGrid'));
const RichTextEditor = lazy(() => import('./RichTextEditor'));

function Dashboard() {
  const [activeTab, setActiveTab] = useState('overview');

  return (
    <div>
      <TabList>
        <Tab onClick={() => setActiveTab('overview')}>Overview</Tab>
        <Tab onClick={() => setActiveTab('analytics')}>Analytics</Tab>
        <Tab onClick={() => setActiveTab('settings')}>Settings</Tab>
      </TabList>

      <Suspense fallback={<TabLoader />}>
        {activeTab === 'overview' && <OverviewTab />}
        {activeTab === 'analytics' && (
          <div>
            <Chart data={analyticsData} />
            <DataGrid rows={tableData} />
          </div>
        )}
        {activeTab === 'settings' && <SettingsTab />}
      </Suspense>
    </div>
  );
}

Feature Flags

function App({ features }) {
  return (
    <div>
      <Header />
      <main>
        <Suspense fallback={<Spinner />}>
          {features.dashboard && (
            <LazyRoute path="/dashboard" component="./Dashboard" />
          )}
          {features.advanced && (
            <LazyRoute path="/advanced" component="./Advanced" />
          )}
        </Suspense>
      </main>
    </div>
  );
}

// Dynamic route component
function LazyRoute({ path, component }) {
  const Component = lazy(() => import(component));
  return (
    <Route path={path} element={<Component />} />
  );
}

Loading States

// Skeleton loader for split components
function ComponentLoader() {
  return (
    <div className="component-skeleton">
      <div className="skeleton-header" />
      <div className="skeleton-content">
        <div className="skeleton-line" />
        <div className="skeleton-line" />
        <div className="skeleton-line" />
      </div>
    </div>
  );
}

// Error boundary for failed loads
class LazyErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return (
        <div className="load-error">
          <p>Failed to load component</p>
          <button onClick={() => window.location.reload()}>
            Reload
          </button>
        </div>
      );
    }
    return this.props.children;
  }
}

// Usage
<LazyErrorBoundary>
  <Suspense fallback={<ComponentLoader />}>
    <LazyComponent />
  </Suspense>
</LazyErrorBoundary>

Practice Problems

0/3solved
Build Code Splitting Component

Create a reusable React component implementing Code Splitting. Include proper state management and accessibility.

Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where needed
Code Splitting Testing

Write unit and integration tests for Code Splitting using React Testing Library.

Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility tests
Code Splitting Performance

Optimize Code Splitting for performance. Consider memoization, code splitting, and bundle size.

Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysis

Quiz

1. What is code splitting?

Question 1 options

2. What does React.lazy do?

Question 2 options

3. Why use route-based splitting?

Question 3 options

4. What is webpackPrefetch?

Question 4 options

5. Why use Suspense with lazy loading?

Question 5 options

Flashcards

Question

What is code splitting?

Answer

Breaking your bundle into smaller chunks that are loaded on demand.

Question

How does React.lazy work?

Answer

It dynamically imports a component when it's first rendered, enabling code splitting.

Question

What is route-based splitting?

Answer

Splitting code based on routes, loading only the JavaScript needed for the current page.

Question

What is the purpose of Suspense?

Answer

To show a fallback UI while lazy components are loading.

Question

What is Code Splitting?

Answer

Code Splitting is a key concept in frontend development.

Revision Notes

Key Takeaways

  • 1.Code splitting reduces initial bundle size by loading code on demand
  • 2.React.lazy and Suspense enable component-level code splitting
  • 3.Route-based splitting loads only code for the current page
  • 4.Prefetching improves perceived performance
  • 5.Always handle loading and error states for lazy components

Interview Tips

  • Explain how React.lazy and Suspense work together
  • Discuss when to use route vs component splitting
  • Know how to handle errors in lazy loaded components

Cheat Sheet

Code Splitting Cheat Sheet

React.lazy

const Component = lazy(() => import('./Component'));

<Suspense fallback={<Spinner />}>
  <Component />
</Suspense>

Route Splitting

const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));

Prefetching

// During idle time
import(/* webpackPrefetch: true */ './Component');

// On hover
const prefetch = () => import('./Component');

Error Handling

  • Use Error Boundary around Suspense
  • Show retry option on failure