Dynamically Instantiate Components

Dynamic component instantiation can help you to avoid loading large modules that you don’t always need. Also, you can instantiate a component instance when the underlying component constructor isn’t known until runtime. Dynamic import is a convenient solution to make a component more customizable. However, it isn’t always the best solution because of the runtime performance overhead it introduces, so don’t overuse it.

To dynamically import and instantiate Lightning web components, you must enable Lightning Web Security.

To instantiate a dynamic component, a component's configuration file must include the lightning__dynamicComponent capability. For example:

To use this capability, you must set the apiVersion property to 55.0 or later.

For more information on a component's configuration file, see Component Configuration File.

To instantiate a component dynamically, use the <lwc:component> managed element with the lwc:is directive in a component's HTML file.

Here's an HTML template that uses <lwc:component>.

<lwc:component> serves as a placeholder in the DOM that renders the specified dynamic component. You must use <lwc:component> with the lwc:is directive.

The lwc:is directive provides an imported constructor at runtime to the <lwc:component> managed element. lwc:is accepts an expression that resolves to a LightningElement constructor at runtime.

If the constructor is falsy, the <lwc:component> tag along with all of its children aren't rendered.

If the expression value is defined but not a LightningElement constructor, an error is thrown.

In the component's JavaScript file, import the custom element using the import() dynamic import syntax.

The import() call returns a promise that resolves to a LightningElement constructor. The element is then rendered instead of the lwc:component placeholder. The tag name used for the dynamic component is the value that's returned for the given constructor.

Similar to a regular component, the dynamic component is instantiated and attached to the DOM. If the dynamic component's constructor changes, the existing element is removed from the DOM.

In this example, the following HTML is rendered after the import completes.

Instead of using the then() method, you can alternatively use the async and await operators to return the component constructor.

A custom element must be attached to the DOM before you can select it. To select a dynamic component, use the lwc:ref directive or use an attribute that's assigned to the component, such as a class name.

To identify if a dynamic component is attached to the DOM:

  • Use connectedCallback in the dynamic component to signal when it's attached to the DOM.
  • Use renderedCallback on the parent component to detect when the dynamic component has rendered to the DOM.

All supported HTML attributes that can be applied to an HTMLElement can also be applied to lwc:component.

Some examples include:

You can include child elements on the dynamic component. <lwc:component> first renders the dynamic component and then its children. Each time the dynamic component changes, the existing element is removed from the DOM along with all of its children. The new dynamic component is then rendered along with its children.

You can use dynamic components in managed packages only. Dynamic components in unlocked packages aren't supported.

Since dynamic imports are “dynamic” by nature, the framework doesn’t prefetch those modules in advance, which can sometimes be detrimental to user experience.

When you use a static import statement, the framework delivers the component and all its dependencies in a single JavaScript file at runtime. To fetch a component that is dynamically imported, the framework has to do a network roundtrip if the content isn’t already stored in the browser HTTP cache.

In this example, the BundleExample component class is served with the StaticImport component class to the browser as a single JavaScript module. The DynamicImport component class is retrieved at runtime when the loadModule function is invoked.

Consider these recommendations when working with dynamic components.

While not currently implemented, future framework optimizations can optimize code where dynamic imports are statically analyzable. Pass a JavaScript string literal to the import() function:

There are always cases where dynamic imports can’t be statically analyzed. This scenario is especially true when the component name is defined via metadata. For all other cases, we strongly recommend making all your dynamic imports statically analyzable to avail of future framework optimizations.

Dynamic import is a convenient solution to make a component more customizable. However, it isn’t always the best solution because of the runtime performance overhead it introduces. Let's illustrate the potential pitfalls with some examples.

In this first example, we create a chart component, c/chart. It accepts chart type as a public property that can be set to either be bar, pie, or line. Internally, the component uses one of the c/barChart, c/pieChart, or c/lineChart components to render the chart based on the type.

If the total bundle size of c/barChart, c/pieChart, and c/lineChart is small, it's preferable to update the c/chart component to use static imports to avoid the network roundtrip at runtime. Generally, we recommend that you start with static import and use dynamic import if performance becomes an issue due to import of components that aren't strictly needed.

If statically importing all three modules negatively impacts runtime performance due to the bundle size increase, it’s still possible to update the example to turn the non-statically analyzable dynamic import into a statically analyzable one.

Here's another example illustrating this principle. In the example, we create a c/field component that is in charge of rendering an entity field value. Since it's a generic component, it accepts a renderer public property that is the name of the component to use to render this field. Unlike in the previous example, the list of known renderers isn’t known in advance because the c/field component potentially accepts any component name.

A more performant approach would be for the c/field component to accept the renderer constructor as a public property instead of the renderer component name. This only works if the component isn’t exposed to a builder, such as the Lightning App Builder.

In this alternative design, the field component delegates to its parent the loading of the renderer component class. The parent component can now use either a static or a dynamic import depending on its requirements.

See Also