Skip to Content
Course content

138: Lazy Loading and Code Splitting Basics

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Why bother splitting my code into different chunks?

Look, when you're first starting out, it feels easier to just have one big app.js file. But as your project grows, that file becomes a monster. If you're using a library like Chart.js or a heavy PDF generator, you're forcing every single user to download that massive amount of code the second they land on your homepage—even if they never actually click the "View Reports" button.

I've seen sites where the initial bundle is 2MB+, and the page just hangs there while the browser tries to parse it all. Code splitting allows you to break that one giant blob into smaller "chunks." You ship the bare essentials first, and then you fetch the heavy stuff only when it's actually needed. It's the difference between handing someone a whole encyclopedia when they only asked for one definition, and just giving them the page they need.

How do I actually implement lazy loading in plain JavaScript?

You're probably used to static imports at the top of your file, like import { calculate } from './math.js';. Those are resolved at load time, which is exactly what we're trying to avoid here. To lazy load, you use the dynamic import syntax: import().

Unlike static imports, import() is a function that returns a Promise. Here is a real-world scenario: imagine you have a heavy analytics.js module that handles complex data processing. You don't want it loading until the user clicks the "Analyze" button.

// This is our main app logic
const analyzeBtn = document.querySelector('#analyze-btn');

analyzeBtn.addEventListener('click', async () => {
    try {
        // The browser only downloads analytics.js when this line is hit
        const analytics = await import('./modules/analytics.js');
        
        // Now we can use the functions exported from that module
        const result = analytics.processData(window.userData);
        console.log('Analysis complete:', result);
    } catch (err) {
        console.error('Failed to load the analytics module:', err);
    }
});

What should I be splitting, and what should I leave alone?

This is where a lot of developers over-engineer things. You don't need to split every single file into its own chunk; that actually hurts performance because you'll end up making dozens of tiny HTTP requests.

I usually follow a few simple rules of thumb:

  • Heavy Libraries: If a module imports a huge third-party package (like a rich-text editor or a mapping API), split it.
  • Conditional Routes: If you have a "User Settings" page or an "Admin Dashboard," those should definitely be lazy-loaded. Most users aren't in the admin panel 99% of the time.
  • Interaction-based Logic: Code that only runs after a specific user action (like opening a complex modal) is a prime candidate for import().

If the code is small and used on every page, just keep it in the main bundle. Don't make the browser work harder than it has to.

How do I handle the "gap" while the code is downloading?

Since import() is asynchronous, there is a window of time where the user has clicked a button but the code hasn't arrived yet. If you leave the UI frozen, the user will think the app is broken.

I always recommend a simple loading state. Since we're dealing with a Promise, you can trigger a spinner or a "Loading..." message right before the await call and remove it once the module is resolved. It's a small detail, but it's what makes a professional app feel snappy rather than glitchy.




📋 Practical Task

Build a Lazy-Loaded "Export to CSV" Feature

You are building a data table. To keep the initial page load fast, you need to move the CSV export logic into its own module and load it only when requested.

  • Create a file named exporter.js that exports a single function exportToCSV(data). This function should simply console.log "Exporting data to CSV..." followed by the data provided.
  • In your main JavaScript file, create a button with the ID #export-btn and a div with the ID #status.
  • Add a click event listener to the button that:
    1. Updates the #status text to "Loading exporter..."
    2. Uses a dynamic import() to load exporter.js.
    3. Calls the exportToCSV function passing in a dummy array of data.
    4. Updates the #status text to "Export Complete!" once the process is finished.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.