Introduction to Import Maps

What Problem Do Import Maps Solve?

Let's say you want to use Lodash, a popular JavaScript utility library, in your project. Traditionally, you had a few options:

  1. Download and link directly:
<script src="lodash.min.js"></script>
<script src="your-app.js"></script>

This pollutes the global scope with _ and makes dependency management messy.

  1. Use a bundler like webpack:
    This requires Node.js, complex build configurations, and a compilation step every time you change code.

Import maps solve this by letting you write clean, readable imports while the browser handles the complexity.

What Are Import Maps?

Import maps are a web standard that tells the browser where to find JavaScript modules. They let you write:

import _ from 'lodash';

The browser uses a special JSON configuration to map the short name 'lodash' to the actual location where the code lives.

Import maps work in all modern browsers.

Setting Up Lodash with NPM (No Bundler)

Let's create a simple example using npm packages directly - no webpack, no build step.

Click Tools -> npm Pacakges, enter lodash-es, add the package to your project.

After generation, PHPMaker will run npm update to download Lodash into node_modules/lodash-es/. We use lodash-es because it's the ES module version that works with import maps.

Create a Custom File with Include common files ENABLED. Enter the follows to the content of the file:

<h1>Lodash Import Map Demo</h1>

<p>Click a button to see Lodash in action:</p>

<button id="chunk-btn">Split Array into Chunks</button>
<button id="shuffle-btn">Shuffle Array</button>
<button id="sum-btn">Calculate Sum</button>

<div id="result" class="result">
    Click a button to see the result!
</div>

<!-- The Import Map -->
<script<?= Nonce() ?> type="importmap">
{
    "imports": {
        "lodash": "./node_modules/lodash-es/lodash.js"
    }
}
</script>

<!-- Your Application Code -->
<script<?= Nonce() ?> type="module">
    import _ from 'lodash';
    
    const result = document.getElementById('result');
    const numbers = [1, 2, 3, 4, 5, 6, 7, 8];
    
    document.getElementById('chunk-btn').addEventListener('click', () => {
        const chunked = _.chunk(numbers, 3);
        result.innerHTML = `
            <strong>Original Array:</strong><br>
            [${numbers.join(', ')}]<br><br>
            <strong>Split into groups of 3:</strong><br>
            ${JSON.stringify(chunked)}
        `;
    });
    
    document.getElementById('shuffle-btn').addEventListener('click', () => {
        const shuffled = _.shuffle(numbers);
        result.innerHTML = `
            <strong>Original Array:</strong><br>
            [${numbers.join(', ')}]<br><br>
            <strong>Shuffled:</strong><br>
            [${shuffled.join(', ')}]
        `;
    });
    
    document.getElementById('sum-btn').addEventListener('click', () => {
        const sum = _.sum(numbers);
        result.innerHTML = `
            <strong>Array:</strong><br>
            [${numbers.join(', ')}]<br><br>
            <strong>Sum:</strong><br>
            ${sum}
        `;
    });
</script>

Generate Scripts and run your site in browser. Go to the custom file and click the buttons to test.

Breaking It Down

The Import Map

<script type="importmap">
{
    "imports": {
        "lodash": "./node_modules/lodash-es/lodash.js"
    }
}
</script>

This tells the browser: "When you see import ... from 'lodash', load the file from ./node_modules/lodash-es/lodash.js"

Important:

  • The import map must come before any module scripts
  • Use type="importmap" (not type="module")
  • The path is relative to your file

Using Lodash

<script type="module">
    import _ from 'lodash';
    
    const numbers = [1, 2, 3, 4, 5, 6];
    const chunked = _.chunk(numbers, 2);
    console.log(chunked); // [[1, 2], [3, 4], [5, 6]]
</script>
  • Use type="module" to enable imports
  • The _ variable contains all Lodash functions
  • No build step needed!

Why This Is Better Than Traditional Methods

Traditional way (global script):

<script src="lodash.min.js"></script>
<script>
    // Lodash is now in global scope as "_"
    const result = _.chunk([1, 2, 3], 2);
</script>

:cross_mark: Global scope pollution
:cross_mark: No module system
:cross_mark: Hard to manage dependencies

Import Maps way:

<script type="importmap">
    {"imports": {"lodash": "./node_modules/lodash-es/lodash.js"}}
</script>
<script type="module">
    import _ from 'lodash';
    const result = _.chunk([1, 2, 3], 2);
</script>

:white_check_mark: Clean imports
:white_check_mark: Module scope
:white_check_mark: Easy to manage
:white_check_mark: No build step needed

Common Beginner Mistakes

:cross_mark: Wrong Lodash package:

npm install lodash  # This won't work with import maps!

:white_check_mark: Use lodash-es:

npm install lodash-es  # ES modules version

:cross_mark: Wrong import map order:

<script type="module">
    import _ from 'lodash';  // Error: import map not loaded yet!
</script>
<script type="importmap">
    {"imports": {"lodash": "..."}}
</script>

:white_check_mark: Import map first:

<script type="importmap">
    {"imports": {"lodash": "..."}}
</script>
<script type="module">
    import _ from 'lodash';  // Works!
</script>

The Problems of Using Import Map with Vanilla JavaScript

You had to:

  1. Find the exact path in node_modules
  2. Manually add it to your import map
  3. Manage updates manually

For one package, this is manageable. But if you use many packages, it gets messy fast.

In next topic, I'll explain how Symfony makes this simpler.