Instantiate LWC components dynamically

From the Winter’24 release, we can import and instantiate Lightning Web Components dynamically. LWC finally reached this feature, that Aura has from a long time.

From the Winter’24 release, we can import and instantiate Lightning Web Components dynamically. LWC finally reached this feature, that Aura has from a long time.

This approach can help you prevent the unnecessary loading of extensive modules that may not be consistently required in your application, or when you're unsure of the specific component constructor until the application is running.

To be able to instantiate an LWC component, lightning__dynamicComponent capability needs to be added to the component’s configuration file.

markup
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>59.0</apiVersion>
    <capabilities>
        <capability>lightning__dynamicComponent</capability>
    </capabilities>
</LightningComponentBundle>

The minimum required api version is 55.0.

To dynamically instantiate the component use <lwc:component> element with lwc:is directive that passes the component contructor.

markup
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>59.0</apiVersion>
    <capabilities>
        <capability>lightning__dynamicComponent</capability>
    </capabilities>
</LightningComponentBundle>

Component contractor can be obtain by using the import() syntax.

javascript
import { LightningElement } from "lwc";
export default class DynamicCmp extends LightningElement {
    componentConstructor;

    connectedCallback() {
        import("c/concreteComponent")
            .then(({ default: ctor }) => (this.componentConstructor = ctor))
            .catch((err) => console.log("Error importing component"));
    }
}

As long as concrete component expose the property with @api decorator, it can be passed via <lwc:component> element.

javascript
import { LightningElement, api } from "lwc";

export default class ConcreteCmp extends LightningElement {
    @api text;
}
markup
<template>
    <div class="container">
        <lwc:component lwc:is={componentConstructor}></lwc:component>
    </div>
</template>

Considerations

Lightning Web Security must be enabled.

Dynamic components work either outside the packages or in Managed packages only - unlocked packages are unsupported.

LWR Sites for Experience Cloud supports only statically analyzable dynamic imports. For this use case, import("c/analyzable") works, but import("c/" + "analyzable") doesn’t work because it isn’t statically analyzable.

Performance As the name suggests, dynamic imports are loaded "on-the-fly", meaning they are not pre-loaded by the system. This can sometimes slow down user experience, as the system needs to fetch these modules when they're actually needed.

That means - use it consciously!

javascript
import { LightningElement } from "lwc";
export default class DynamicCmp extends LightningElement {
    componentConstructor;

    connectedCallback() {
        import("c/concreteComponent")
            .then(({ default: ctor }) => (this.componentConstructor = ctor))
            .catch((err) => console.log("Error importing component"));
    }
}

Text and code were extracted from the original slide. Plain-text version of the whole catalog