---
schema_version: "1"
id: a58596436616a1829d0c97310505a4b7314c5d16d03f22e4002a36ac9e07cc23
title: Module Providers for Lightning Web Runtime on Node.js
collection: developer
version: current
locale: en-us
portal_url: https://docs.salesforce.com/developer/current/en-us/lwr-node/lwr/guides/lwr-module-providers.md
canonical_url: https://developer.salesforce.com/docs/platform/lwr/guide/lwr-module-providers.html
source_site: developer.salesforce.com
content_type: documentation
content_hash: 6f2c477ddf1deccc987c6b5bf5e4583413cf96a92017559eb3c559b433a48f91
products:
  - Lightning Web Runtime
---
# Module Providers for Lightning Web Runtime on Node.js

**Module providers** locate and serve modules for your web app. Your project's LWR server contains a **module registry**, which maintains a list of available module providers and helps match **module requests** to a valid module provider.

All Lightning Web Runtime on Node.js (LWR-JS) apps automatically include [default module providers](#default-module-providers) that support common module types, like static file system modules and packaged Lightning web components (LWCs). If you want to use ECMAScript (ES) modules from other sources, you can [import a module provider](#import-a-module-provider) or [create a custom module provider](#create-a-custom-module-provider).

This flexibility ensures that your runtime is small for simple use cases, but LWR-JS can support more complex applications if needed.

## Default Module Providers

Your project automatically includes these module providers.

```
'@lwrjs/app-service/moduleProvider',  // Application Bootstrap Module Provider
'@lwrjs/router/module-provider',      // Router Module Provider
'@lwrjs/lwc-module-provider',         // LWC Module Provider
'@lwrjs/npm-module-provider',         // Node Package Manager (npm) Module Provider
'@lwrjs/module-registry/externals-module-provider'    //
```

### Application Bootstrap Module Provider

This module provider generates only the Application Bootstrap Module. You need it for your website to load correctly.

### Router Module Provider

The [Router Module Provider](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-create-router.md#router-module-provider) generates a router based on a static JSON file.

### LWC Module Provider

The LWC Module Provider locates and serves Lightning Web Components (LWC) modules for your app. LWCs are special instances of ES modules that extend the [`LightningElement`](https://developer.salesforce.com/docs/platform/lwc/guide/create-components-javascript.html#ui-component) class. They have to be processed by the LWC compiler before a module provider can serve them.

### npm Module Provider

This module provider locates and serves all of the Node Package Manager (npm) ES modules from your project's `node_modules` directories.

### Externals Module Provider

The LWR-JS Externals Module Provider creates module definitions based on the externals in your project's [bundle configuration (`bundleConfig`)](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-configure-project.md#configure-bundles).

## Import a Module Provider

With LWR-JS, you can add an existing module provider to your project. For example, the LWR [Label Module Provider](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-label-module-provider.md) exposes capabilities for scoped module label support (`@salesforce/label/*`). You can add this tooling to your application via the `@lwrjs/label-module-provider` npm package.

To add an optional module provider to your project, first install the package using `npm` on the command line interface (CLI). For example, this code installs the Label Module Provider.

```bash
npm install @lwrjs/label-module-provider
```

Then, add the module provider plugin to your `lwr.config.json` file. Remember to include the default module providers in the `moduleProviders` array. Here's how to add the Label Module Provider to your app's config file.

**important:** The `moduleProviders` array overwrites the default LWR array. You have to list **all** the module providers needed by your app, including the default LWR ones listed in [Default Module Providers](#default-module-providers).

```
{
  "moduleProviders": [
    "@lwrjs/label-module-provider",       // Optional provider
    "@lwrjs/app-service/moduleProvider",  // Default provider
    "@lwrjs/router/module-provider",      // Default provider
    "@lwrjs/lwc-module-provider",         // Default provider
    "@lwrjs/npm-module-provider",         // Default provider
    "@lwrjs/module-registry/externals-module-provider"    // Default provider
  ]
}
```

That configuration lets your application components statically import labels sourced from JSON files in your project’s `$rootDir/src/labels` directory, like this:

```javascript
import GREETING from "@salesforce/label/home.greeting";
```

### Optional Supported LWR-JS Module Providers

You can add these module providers to your LWR-JS project.

- [Label Module Provider](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-label-module-provider.md)

## Create a Custom Module Provider

If you want your app to use a custom module that can't be served by one of the default module providers, add a custom module provider to your project. You can create a custom ES provider or a [custom LWC provider](#create-a-custom-lwc-module-provider).

In this section, we'll walk through how to create and implement a custom ES provider. We can boil this process down into three steps.

1. Create the module provider.
2. Configure `getModuleEntry()` and `getModule()`.
3. Register the module provider.

### Create a Custom ES Module Provider

To create an ES module provider, implement the `ModuleProvider` LWR interface. You have to give the module provider a name, but the `version` property is optional.

```
import { ModuleProvider } from 'lwr';
export default class MyProvider implements ModuleProvider {
    name = 'echo-provider';
    private version = '1';
}
```

Save this file in your app's `src` folder. For example, the sample module provider `es-custom-provider` is nested in `src/services`.

```
build/
node_modules/
src/
  ├── modules/
  ├── services/
  │   ├── es-custom-provider.ts
  │   └── lwc-custom-provider.ts
  └── index.ts
lwr.config.json
package.json
```

### Configure `getModuleEntry()` and `getModule()`

Module providers rely on `getModuleEntry()` and `getModule()` to match module requests to module providers and help compile modules. You should customize these functions for your custom module provider.

#### `getModuleEntry()`

When your LWR server receives a request for a module – like someone loading a webpage – the module registry calls `getModuleEntry()` on all of its module providers. The first module provider to correctly handle this request is assigned to serve the requested module.

The module registry passes these two arguments into `getModuleEntry()`.

- A **module ID**, which contains the module's `specifier` (a unique ID value).
- `RuntimeParams`, a map of unique requirements for the requested page. For example, it could include a requirement for a specific `locale`.

The function returns a `ModuleEntry` object that contains information from a valid module provider and the module request.

You should customize the information in `ModuleEntry` based on your custom module provider and the module it serves.

- `id` - At a minimum, include the module's `specifier` and the module provider's `version`. If the module is locale-specific, include the `locale` value from `RuntimeParams`.
- `virtual` - If module code from your provider is generated from scratch, set this to `true`. If it's read off of a file system or another source, set this to `false`.
- `entry` - Set this to the module's filepath. If the module is virtual, ensure the path includes the `specifier`.
- `version` - Set this to the module provider's version.

Here's an example of a `ModuleEntry` object returned by a custom module provider.

```
import { AbstractModuleId, ModuleCompiled, ModuleEntry, ModuleProvider, RuntimeParams } from 'lwr';

export default class MyProvider implements ModuleProvider {
    async getModuleEntry(
        { specifier }: AbstractModuleId,
        runtimeParams: RuntimeParams = {},
    ): Promise<ModuleEntry | undefined> {
        if (specifier.startsWith('@my/')) {         // Module provider checks for the @my namespace
            return {
                id: `${specifier}|${this.version}`,
                virtual: true,
                entry: `<virtual>/${specifier}.js`,
                specifier,                          // Reuse the specifier passed into getModuleEntry()
                version: this.version,
            };
        }
    }
}
```

#### `getModule()`

When the module registry validates that a module provider can fulfill a module request, the registry calls `getModule()`. `getModule()` takes the same arguments as `getModuleEntry()`. It returns a `ModuleCompiled` object.

```
import { AbstractModuleId, ModuleCompiled, ModuleEntry, ModuleProvider, RuntimeParams } from 'lwr';
import { hashContent } from '@lwrjs/shared-utils';

// Return a generated ES code string
function generateModule(specifier: string): string {
    return `...`;
}

export default class MyProvider implements ModuleProvider {
    async getModule(
        { specifier, namespace, name }: AbstractModuleId,
        runtimeParams: RuntimeParams = {},
    ): Promise<ModuleCompiled | undefined> {
        // Retrieve the Module Entry
        const moduleEntry = await this.getModuleEntry({ specifier });
        if (!moduleEntry) {
            return;
        }

        // Generate code for the requested ES module
        const originalSource = generateModule(specifier);

        // Construct a Module object
        return {
            id: moduleEntry.id,
            specifier,
            namespace,
            name,
            version: this.version,
            originalSource,
            moduleEntry,
            ownHash: hashContent(originalSource),
            // Note: there is no need to compile this module
            // The module registry will compile ES code, if needed
            compiledSource: originalSource,
        };
    }
}
```

### Register a Custom Module Provider

To register a custom module provider, add it to your project's `lwr.config.json` file. Make sure that TypeScript has been transpiled into JavaScript.

```
{
    "moduleProviders": [
        "$rootDir/build/services/es-custom-provider.js",
        "$rootDir/build/services/lwc-custom-provider.ts",
        "@lwrjs/app-service/moduleProvider",
        "@lwrjs/lwc-module-provider",
        "@lwrjs/npm-module-provider"
    ]
}
```

**important:** The `moduleProviders` array overwrites the default LWR array. You have to list **all** the module providers needed by your app, including the default LWR ones listed in [Default Module Providers](#default-module-providers).

Alternatively, you can pass your configuration from `lwr.config.json` to a module provider constructor. The configuration can be any JSON object or primitive type.

```
{
    "moduleProviders": [
        [
            "$rootDir/build/services/provider-with-config.js",
            {
                "cache": true,
                "locales": ["en", "es", "de"]
            }
        ]
    ]
}
```

The configuration and `ProviderContext` (from the LWR server) are passed into the module provider constructor.

```
interface MyProviderOptions {
    cache?: boolean;
    locales?: string[];
}
export default class MyProvider implements ModuleProvider {
    constructor({ cache = true, locales = [] }: MyProviderOptions, context: ProviderContext) {
        // initialization
    }
}
```

## Create a Custom LWC Module Provider

LWCs are special instances of ES modules that extend the `LightningElement` class. Before a module provider serves LWCs, they have to be processed by the LWC compiler.

The easiest way to create a custom LWC module provider is to extend the `LwcModuleProvider` class from the package `@lwrjs/lwc-module-provider`.

To adapt the LWC module provider for your LWR app, override `getModuleEntry()` and `getModuleSource()`. The `getModule()` function of the superclass handles compilation of the LWC.

```
import path from 'path';
import LwcModuleProvider from '@lwrjs/lwc-module-provider'; // add to package.json dependencies
import {
    AbstractModuleId,
    FsModuleEntry,
    ModuleCompiled,
    ModuleEntry,
    ModuleProvider,
    ModuleSource,
} from 'lwr';
import { hashContent } from '@lwrjs/shared-utils';

// Return generated LWC code strings by file type: html, css or default js
function generateModule(specifier: string): string {
    const fileType = path.extname(specifier).substring(1);
    switch (fileType) {
        case 'html':
            return `<template><!-- HTML code --></template>`;
        case 'css':
            return `/* CSS code */`;
        default:
            // 'js'
            return `
import { LightningElement } from 'lwc';
export default class MyLwc extends LightningElement { /* LWC code */ }`;
    }
}

export default class MyLwcProvider extends LwcModuleProvider implements ModuleProvider {
    name = 'lwc-provider';
    private version = '1';

    async getModuleEntry({ specifier }: AbstractModuleId): Promise<FsModuleEntry | undefined> {
        if (canHandle(specifier)) {
            return {
                id: `${specifier}|${this.version}`,
                // Incoming specifiers may be html or css code
                // Ensure the entry file extension matches:
                entry: `<virtual>/${specifier}${path.extname(specifier) ? '' : '.js'}`,
                specifier,
                version: this.version,
            };
        }
    }

    getModuleSource(
        { name, namespace, specifier }: AbstractModuleId,
        moduleEntry: ModuleEntry,
    ): ModuleSource {
        const originalSource = generateModule(specifier);
        return {
            id: moduleEntry.id,
            specifier,
            namespace,
            name: name || specifier,
            version: moduleEntry.version,
            moduleEntry,
            ownHash: hashContent(originalSource),
            originalSource,
        };
    }

    // This method handles LWC compilation => let the superclass handle this processing
    // It calls `getModuleSource` under the covers
    async getModule(moduleId: AbstractModuleId): Promise<ModuleCompiled | undefined> {
        return super.getModule(moduleId);
    }
}
```
