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.
In Jest tests, the component's tag name for a dynamic component is an internal default value. To select the dynamic component in a Jest test, we recommend that you use a different selector by appending a custom data attribute.
In the Jest test, select the component using this syntax.
For more information, see Write Jest Tests for Lightning Web Components.
All supported HTML attributes that can be applied to an HTMLElement
can also be applied to lwc:component
.
Some examples include:
- Standard global HTML attributes
- Custom HTML attributes, such as
data-*
- Event listeners
Dynamic components behave like standard Lightning web components. lwc:component
supports the directives for HTML elements, except for lwc:external
.
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.
Passing a property to a dynamic component is similar to passing a property to a child component. In the dynamic component, annotate the property with @api
and use it in the template.
In the placeholder component, import your custom element.
Pass in the value for the text
property.
In certain cases, it might not be possible to set all the potential properties a dynamic component can accept via the standard markup syntax. For example, when the component to be instantiated isn't known in advance, or the components to be instantiated accept a different set of public properties.
In those specific cases, the lwc:spread
directive can be used to dynamically set to dynamic component properties at runtime. lwc:spread
also enables elements to accept an object that's bound as properties at runtime.
Make the properties public by annotating them with @api
.
Use the properties in your template.
Import your custom element as usual, and create a childProps
object with the property name and values.
Use lwc:spread
to pass in your properties, which then renders "San Francisco, CA" on the dynamic component.
A dynamic component can load data based on record context. To retrieve the record ID on a page, pass the record ID value to the dynamic component. Passing the record ID to a dynamic component is similar to passing a property to a child component.
Retrieve the component module at runtime.
The record ID is passed to the dynamic component and you can use it to retrieve record data based on the ID.
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
- Lightning Web Security
- Salesforce Developers' Blog: Dynamic Components for Lightning Web Components Is Now GA