# @lwrjs/loader

> LWR's client-side module loader

Latest version **0.24.1** (published 2026-09-15) · MIT license · 0 weekly downloads

## Install

```sh
npm install @lwrjs/loader
pnpm add @lwrjs/loader
yarn add @lwrjs/loader
bun add @lwrjs/loader
```

## Health

**Score 65/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; recently updated; high maintenance score.

Warnings: low downloads; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.24.1 |
| Published | 2026-09-15 |
| First published | 2020-07-27 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | >=22.0.0 |
| Dependencies | 2 |
| Unpacked size | 785.2 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Maintainers | nrkruk, lpomerleau, lwc-admin, kmbauer, kevinv11n |

## Links

- npm: https://www.npmjs.com/package/@lwrjs/loader
- Repository: https://github.com/salesforce-experience-platform-emu/lwr
- Homepage: https://developer.salesforce.com/docs/platform/lwr/overview
- Issues: https://github.com/salesforce-experience-platform-emu/lwr/issues
- npm.io page: https://npm.io/package/@lwrjs/loader

## Dependencies (2)

- [@lwrjs/shared-utils](https://npm.io/package/@lwrjs/shared-utils.md) 0.24.1
- [@lwrjs/client-modules](https://npm.io/package/@lwrjs/client-modules.md) 0.24.1

## Recent versions

- 0.24.1 (latest) — 2026-09-15
- 0.23.22 (summer26) — 2026-09-23
- 0.19.9 (winter26) — 2026-09-15
- 0.22.21 (vanilla) — 2026-09-03
- 0.22.7-alpha.6 (alpha) — 2026-03-02
- 0.21.3 (next) — 2026-01-14
- 0.17.9 (summer25) — 2025-05-20
- 0.15.10 (patch) — 2025-03-28
- 0.13.10 (winter25) — 2024-10-28
- 0.12.4 (summer24) — 2024-06-21
- 0.11.15 (spring24) — 2024-03-01
- 0.9.6 (0.9-patch) — 2024-01-12
- 0.10.14 (winter24) — 2023-12-08
- 0.7.8 (0.7-patch) — 2022-10-13
- 0.5.14 (core-236-patch) — 2022-03-09
- … 599 more at https://npm.io/package/@lwrjs/loader/versions

## README

# Lightning Web Runtime :: Module Loader

This package contains the client-side AMD (Async Module Definition) module loader for Lightning Web Runtime (LWR).

_The LWR loader is inspired and borrows from the algorithms and native ESM loader principles of <https://github.com/systemjs/systemjs>. The north star of the LWR loader is to align with functionality and behavior of the ESM loader._

## Basic Example

```js
import Loader from 'lwr/loader';

const loader = new Loader();

// Set the define on the global scope, matching the module define call from the server
globalThis.LWR.define = loader.define;

// load a module by URL -- assume fetched module is AMD
loader.load('/path/to/foo/bar').then((module) => {
    console.log(module.default);
});

// define/preload a module then load it
loader.define('foo/baz', [], function () {
    return 'foo/baz';
});
loader.load('foo/baz').then((module) => {
    console.log(module.default); // foo.baz
});
```

## API

### `class Loader`

```ts
class Loader {
    constructor(baseUrl? string){...}
}
```

A class used to construct a loader instance.

**Parameters**

-   `baseUrl` (string - optional) Set the base URL for all resolved URLs. By default the loader will attempt to parse the baseUrl from the document (if there is a document).

### `loader.define()`

```ts
interface Define {
    (name: string, dependencies: string[], exporter: Function, signatures: ModuleDefinitionSignatures): void;
}

type ModuleDefinitionSignatures = {
    ownHash: string;
    hashes: {
        [key: string]: string;
    };
};
```

LWR's AMD-like module format support. Unlike generic AMD, all modules in LWR must be named.

**parameters**

-   `name` - The module name
-   `dependencies` - A list of module dependencies (module imports)
-   `execute` - The function containing the module code.
-   `signatures` - An argument unique to LWR's AMD loader. The object containing the "signature" of the module and the signatures for each of its dependencies. Signatures are used to identify module changes from the server and is discussed at length in the [Module API RFC](https://rfcs.lwc.dev/rfcs/lwr/0002-component-api#signatures).

In order to load modules from the server, you must set the define on the global scope matching the module define call from the server:

```js
const loader = new Loader();
globalThis.LWR.define = loader.define;
```

Modules can now be defined globally:

```js
LWR.define('foo/bar', ['exports'], (exports) => {
    exports.foo = 'bar';
});
```

### `loader.importMap()`

```ts
interface LwrImportMapUpdateMethod {
    (importMapUpdate: ImportMapUpdate): void;
}

type ImportMapUpdate = Record<string, string[]>;
```

Apply update of import mappings at runtime. This allows dynamically registering new module mappings after the loader has been initialized. This is a synchronous operation.

**parameters**

-   `importMapUpdate` - Import Map Update object containing:
    -   [moduleScriptURL] - Script URL containing LWR define statements
    -   string[] - array of module names which are included in the given script

**Important**: Import map entries are **write-once** — once a specifier is mapped, it cannot be overridden. This ensures deterministic module resolution and prevents runtime mutation of previously resolved dependencies. If a conflicting mapping is provided for an existing specifier, the new mapping is ignored and a warning is logged.

**Important**: Protected modules (`lwc`, `lwr/*`) cannot be remapped for security reasons. Attempting to remap these modules will throw an error.

**Example**

```js
// Register a runtime import mapping
// ONLY AVAILABLE WHEN USING THE GLOBAL API (AMD / Legacy Mode):
LWR.importMap({
    '/bundles/my-module.js': ['my/module'],
});
```

### `loader.load()`

```ts
interface Load {
    (id: string): Promise<Module>;
}
```

Retrieves/loads a module, returning it from the registry if it exists and fetching it if it doesn't. Note, this is functionality equivalent to an [ESM dynamic import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports), returning a `Promise` which resolves to the module.

**parameters**

-   `id` - A module identifier or URL

### `loader.has()`

```ts
interface Has {
    (id: string): boolean;
}
```

Checks if a Module exists in the registry. Note, returns false even if the ModuleDefinition exists but the Module has not been instantiated yet (executed).

**parameters**

-   `id` - A module identifier or URL

### `loader.resolve()`

```ts
interface Resolve {
    (id: string): Promise<string>;
}
```

Returns a promise resolving to the module identifier or URL. Returns the module identifier if the module has been defined, or the full resolved URL if a URL is given.

**parameters**

-   `id` - A module identifier or URL

## Loader ServiceAPI

```ts
interface ServiceAPI {
    addLoaderPlugin: LoaderPluginSetter;
}

interface LoaderPluginSetter {
    (plugin: LoaderPlugin): void;
}

interface LoaderPlugin {
    resolveModule: ResolveModuleHook;
    loadModule: LoadModuleHook;
}
```

## `loader.services.addLoaderPlugin()`

The `loader.services.addLoaderPlugin` method allows defining plugins (i.e. hooks) into the loader.

**parameters**

-   `hooks` (Object) - The `hooks` object contains the available hooks available into the loader. Hooks can be defined multiple times (with subsequent `addLoaderPlugin` calls), and will be used by the loader in the order that they are defined.

**Examples**

```js
loader.services.addLoaderPlugin({
    resolveModule: (id, { parentUrl }) => {
        if (id.startsWith('lightning')) {
            return `/my/api/${id}`;
        }
        // defer back to default loader resolve
        return null;
    },
    loadModule: async (url) => {
        if (url.indexOf('/my/api/') >= 0) {
            return fetch(url);
        }
        // defer back to default loader load
        return null;
    },
});
```

### `resolveModule` hook

```ts
type ResolveParams = {
    parentUrl: string;
};

interface ResolveModuleHook {
    (id: string, params: ResolveParams): ResolveResponse | Promise<ResolveResponse>;
}

type ResolveResponse = URLResponse | string | null;

type URLResponse = {
    url: string;
};
```

The ResolveModuleHook allows hooking into the resolution phase of the module lifecycle. The hook can be synchronous or async (return a `Promise`). If there are multiple `ResolveModuleHooks` defined, each hook (in order) will be called until a `URLResponse` is received.

**parameters**

-   `id` - A module identifier or URL
-   `params` - Object containing additional parameters
-   `parentUrl`
-   the `baseUrl` of the loader

**return value**

Returning the following values from the hook will determine the next steps of the loader:

-   `URLResponse` - The loader will call the `load` phase with the given `url`.
-   `string` - The loader will call the next `ResolveModuleHook` or defer back to the default loader resolution with the returned `string`.
-   `null` - The loader will call the next `ResolveModuleHook` or defer back to the default loader resolution with the original `id`.

### `loadModule` hook

```ts
interface LoadModuleHook {
    (url: string): Promise<LoadResponse> | LoadResponse;
}

type LoadResponse = Response | CustomResponse | null;

type CustomResponse = {
    // the string response for the module request
    data: string;
    // the HTTP status code for the module request - a 200 or 201 value indicates success
    status: number;
    // the HTTP status message for the module request
    statusText?: string;
};
```

The LoadModuleHook allows hooking into the load phase of the module lifecycle. The hook can be synchronous or async (return a `Promise`). If there are multiple `LoadModuleHooks` defined, each hook (in order) will be called until a `CustomResponse` | `Response` is received.

**parameters**

-   `url` - The absolute URL of the module to load

**return value**

Returning the following values from the hook will determine the next steps of the loader:

-   `CustomResponse | Response` - The loader will evaluate the module definition returned from the response.
-   `null` - The loader will call the next `LoadModuleHook` or defer back to the default loader `load` with the given `url`

**note** The `loadModule` hook can be used to serve up arbitrary JavaScript code, which is executed outside of the Locker sandbox. In order to protect their LWR app, the app developer must ensure they are not configuring or including any malicious modules via their loader hooks.

## Bundling Support

The LWR loader supports loading AMD bundles -- multiple modules concatenated in a single file:

**note** When a bundle is loaded & executed without a module name, the _last_ module in the bundle (file) is executed. See examples below.

**note** Module bundlers such as Rollup also support "bundling" of AMD modules via import/export rewriting, instead of module concatenation.

```js
// my/bundle.js
LWR.define('c', [], () => 'c');
LWR.define('b', ['c'], (c) => b + c);
LWR.define('a', ['b'], (b) => a + b);
```

"Preload" the bundle with a script, then execute the 'a' module

```html
...
<script src="/path/to/my/bundle.js" type="application/javascript">
    <script type="application/javascript">
        // assume loader provided globally for this example
        loader.load('a').then((module) => {
            console.log(module.default); // 'abc'
        });
</script>
```

Loads and executes the _last_ module in a bundle:

```js
const { default: result } = await loader.load('/path/to/my/bundle.js');
console.log(result); // 'abc'
```

## Loader Shim

The Client Runtime Loader Shim bootstraps the [Loader instance](#api) and other required modules for LWR applications in AMD format. The AMD Loader Shim is responsible for:

-   defining and executing the [`@lwr/loader`](#api)
-   exposure of the loader's `define` and `importMap` APIs at `globalThis.LWR.define` and `globalThis.LWR.importMap`
-   client-side orchestration for the generated application bootstrap module

### LWR Application Document

A web client provides the following HTML document to bootstrap a LWR application:

```html
<head>
    <title>Lightning Web Application</title>
</head>
<body>
    <x-example></x-example>

    <!-- client bootstrap configuration for c/example app -->
    <script>
        globalThis.LWR = globalThis.LWR || {};
        Object.assign(globalThis.LWR, {
            autoBoot: true,
            bootstrapModule: 'bootstrap/c%2fexample/v/0.0.1',
        });
    </script>

    <!-- preloaded bootstrap modules: AMD Loader Shim and Loader Module -->
    <script src="/1/resource/prod/l/en_US/i/amd-loader-shim.js%2Fv%2F0_0_1"></script>
    <script async src="/1/module/prod/l/en_US/i/lwr%2Floader%2Fv%2F0_0_1"></script>
</body>
```

Read more [here](../view-registry/BOOTSTRAP.md) and in the [bootstrap RFC](https://rfcs.lwc.dev/rfcs/lwr/0000-lwr-bootstrap#web-client-bootstrap-flow).

### Client Bootstrap Config

The LWR server provides configuration used by the loader shim:

```js
globalThis.LWR: {
    // true if there is no customInit hook
    autoBoot: true,
    // versioned application bootstrap module name
    bootstrapModule: '@lwrjs/bootstrap/c/example/v/0_0_1',
    // (optional) modules which must be loaded before the application is mounted
    //    add all preloaded modules to this list
    //    the lwr/loader module is an implied member of this list
    requiredModules: [
        '@lwrjs/bootstrap/c/example/v/0_0_1',
        // ...
    ],
    // (optional) modules being preloaded outside the LWR loader
    // The LWR loader will not send requests to fetch preloadModules
    preloadModules: [
        'c/example/v/1',
        // ...
    ]
    // (optional) versioned root application component names
    rootComponents: ['c/example/v/1', 'c/nav/v/1']
}
```

Read more in the [bootstrap RFC](https://rfcs.lwc.dev/rfcs/lwr/0000-lwr-bootstrap#client-bootstrap-config).

### Custom Initialization

Some host environments may desire to defer or manage when the application is initialized. To do this, the `customInit` bootstrap hook can be implemented by:

-   setting `globalThis.LWR.autoBoot` to `false`
-   adding a `globalThis.LWR.customInit` function

A host environment may set a custom error handler by implementing the `onError` hook:

-   setting a `globalThis.LWR.onError` function

**Note**: An error handler set using `CustomInitAPI.onBootstrapError()` takes precedence over `globalThis.LWR.onError()`.

```ts
type CustomInitFunction = (lwr: CustomInitAPI, config: ClientBootstrapConfig) => void;

// The API argument passed to the customInit hook
type CustomInitAPI = {
    // called to trigger app initilization
    initializeApp: InitializeApp;
    // register bootstrap error state callback
    onBootstrapError: RegisterCustomErrorHandler;
    // Register a dispatcher for the metrics profiler
    attachDispatcher: (dispatcher: LogDispatcher) => void;
    // A convenience pointer to the globally available LWR.define
    define: Function;
};

type ClientBootstrapConfig = {
    autoBoot: boolean;
    bootstrapModule: string;
    rootComponents?: string[];
    requiredModules: string[];
    preloadModules?: string[];
    customInit?: CustomInitFunction;
    onError?: CustomErrorHandler;
    baseUrl?: string;
    endpoints?: Endpoints;
    imports?: ImportMetadataImports;
    index?: ImportMetadataIndex;
};
```

The loader shim calls the `customInit` hook and **will not** start the application until it calls `lwr.initializeApp()`. Triggering `lwr.initializeApp()` before all `requiredModules` are defined will result in an error.

**Note**: The [loader module](#api) is automatically added to the `requiredModules` array by the [Loader Shim](#loader-shim).

```html
<body>
    <x-example></x-example>

    <!-- client bootstrap configuration for c/example app -->
    <script>
        globalThis.LWR = globalThis.LWR || {};
        Object.assign(globalThis.LWR, {
            autoBoot: false,
            bootstrapModule: '@lwrjs/bootstrap/c/example/v/0_0_1',
            customInit: (lwr, config) => {
                // execute custom pre-initialization code
                lwr.initializeApp();
            },
            onError: (error) => {
                // report error
            },
        });
    </script>

    <!-- ... -->
</body>
```

Read more in the [bootstrap RFC](https://rfcs.lwc.dev/rfcs/lwr/0000-lwr-bootstrap#custominit-hook).

### Output

The AMD Loader Shim is pre-built provided as a static IIFE resource:

```fs
build/
  └── assets
      └── prod
          └── lwr-loader-shim.js
```

---
_Source: https://npm.io/package/@lwrjs/loader · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
