Skip to main content

Compose Components

Learning Objectives

After completing this unit, you’ll be able to:
  • Compose components.
  • Use a slot.
  • List differences between data binding behavior for the two programming models.

You can add components within the body of another component. This component composition enables you to build complex components from simpler building-block components.

In a large application, it’s useful to compose the app with a set of smaller components to make the code more reusable and maintainable.

The lightning namespace contains many base components, such as lightning-button, that you can use to build your components. For more details, see the Component Library.

Understand Component Relationships

Let’s start by looking at a simple example and defining some of the terminology we use for composed components. This discussion can seem like an unnecessary diversion, but it’s important to have consistent terminology when you’re talking with other developers.

<!-- owner.html -->
<template>
    <c-child first-name="Moonbeam"></c-child>
</template>

This HTML file uses <c-child>, which refers to the child component in the c namespace. If you haven’t set a namespace prefix for your organization, use the default namespace c when referencing components that you’ve created.

Owner
The owner is the component that owns the template. In this example, the owner is the c-owner component. The owner controls all the composed components that it contains. The owner can:
  • Set public properties on composed components.
  • Call public methods on composed components.
  • Listen for any events fired by the composed components.
Parent and Child
When a component contains another component, which, in turn, can contain other components, we have a containment hierarchy. In the documentation, we sometimes talk about parent and child components. A parent component contains a child component. A parent component can be the owner or a container component that’s also part of the owner’s template. In this example, c-parent-container is a container component because it contains c-child. The c-parent-container component is not the owner because it’s in the template of the owner component, c-owner.
<!-- owner.html -->
<template>
    <c-parent-container>
        <c-child first-name="Moonbeam"></c-child>
    </c-parent-container>
</template>

DreamHouse Example

Let’s look at an example of composing components. The PropertyTileList Aura component contains the PropertyTile component.

<aura:component>
    ...
    <div class="slds-card">
        <lightning:layout horizontalAlign="center" multipleRows="true">
            <aura:iteration items="{!v.properties}" var="property">
                <lightning:layoutItem padding="around-small" size="12" smallDeviceSize="6" mediumDeviceSize="4" largeDeviceSize="3">
                    <c:PropertyTile property="{#property}"/>
                </lightning:layoutItem>    
            </aura:iteration>
        </lightning:layout>
    </div>
</aura:component>

The PropertyTile component has an attribute called property. Don’t be distracted by that name. DreamHouse is a real-estate app, so we’re talking about real-estate properties rather than JavaScript properties here!

Here’s the HTML for the propertyTileList Lightning web component:

<template>
    <div class="slds-card tile-list">
        <template for:each={filteredProperties} for:item="property">
            <c-property-tile property={property} key={property.Id}></c-property-tile>
        </template>
    </div>
</template>

The markup looks similar for the two programming models, except for the syntax for referencing a composed component. We saw earlier how the Aura model and the Lightning Web Components model each have a different syntax for references to components in markup.

  • In the Aura component, we use c:PropertyTile.
  • In the Lightning web component, we use c-property-tile. A dash separates the namespace, c, from the component name. The camel-case propertyTile Lightning web component is referenced as property-tile in the HTML file of another Lightning web component that contains it.

Lightning Web Component Contains a Lightning Web Component

Let’s look at composition with two Lightning web components, lwcContainerComponent and lwcComponent.

<!-- lwcContainerComponent.html -->
<template>
    <c-lwc-component></c-lwc-component>
</template>

To use a Lightning web component, lwcComponent, in another Lightning web component:

  • Use a dash to separate the namespace, c, from the component name.
  • Change the camel case name, lwcComponent, to a kebab case (dash-separated) reference, lwc-component. We use the dash-separated reference for consistency with Web Components standards.
  • The HTML spec mandates that tags for custom elements aren’t self-closing. Self-closing tags end with />. A Lightning web component is essentially a custom element. Therefore, you need opening and closing tags for the component reference:
    <c-lwc-component></c-lwc-component>

Aura Component Contains a Lightning Web Component

Now let’s look at an Aura component, auraWrapper, that contains a Lightning web component, lwcContainerComponent.

<!-- auraWrapper.cmp -->
<aura:component>
    <c:lwcContainerComponent />
</aura:component>

To use a Lightning web component, lwcContainerComponent in an Aura component:

  • Use a colon to separate the namespace, c, from the component name.
  • Use the camel case component name, lwcContainerComponent.
  • Self-closing tags are allowed in Aura markup so we use <c:lwcContainerComponent />.

Facets Become Slots

In Aura, a facet is any attribute of type Aura.Component[]. That’s just a fancy way of saying you can set an array of components for the attribute. In even less fancy terms, think of a facet as a sandwich. You can stuff as many ingredients (components) as you’d like into your sandwich (facet).

For example, every Aura component inherits a body attribute, which is a facet. The concept of facets is important because it allows you to design a component with an attribute that’s a placeholder for markup that a parent component can set.

Lightning web components use slots instead of facets. A slot is a placeholder for markup that a parent component passes into a component’s body. The HTML <slot> element is part of the Web Components standard.

Here’s an example of an Aura component that contains a header facet and the body facet that every component has by default.

<aura:component>
    <aura:attribute name="header" type="Aura.Component[]"/>
    <div>
        <span>{!v.header}</span><br/>
        <span>{!v.body}</span>
    </div>
</aura:component>

A similar Lightning web component looks like this.

<template>
    <div>
        <slot name="header"></slot><br/>
        <slot></slot>
    </div>
</template>

The slot named header replaces the header facet. The unnamed slot replaces the body facet.

You can see a deeper dive in the Lightning Web Components Developer Guide.

Anything Else to Consider About Facets?

Yes, I’m glad you asked! Remember that Lightning web components can’t contain Aura components. If you’re migrating an Aura component that contains a facet, you must also migrate any subcomponents that you want to stuff into the facet. That way, you ensure that you never put an Aura component into a Lightning web component.

Data Binding Differences

When you add a component in markup, you can initialize public property values in the child component based on property values of the owner component. The data-binding behavior is different for the two programming models.

Aura Component Data Binding

Aura components can use two forms of expression syntax for data binding: {!expr} or {#expr}. We won’t go into the differences here, but let’s look at a simple example.

<!-- owner.cmp -->
<aura:component>
    <aura:attribute name="fName" type="String" />
    <c:child firstName="{!v.fName}"/>
</aura:component>

The owner component passes the value of its fName attribute into the child component, which results in a data binding, also known as a value binding, between the two components.

In Aura components, this data binding is two way. Changes to the fName attribute are passed into the child component’s firstName attribute, and changes in the value of the firstName attribute in the child component are passed back to the fName attribute of the owner component. This bidirectional data binding can be expensive for performance, and can create hard-to-debug errors due to the propagation of data changes, especially if you have deeply nested components.

Lightning Web Component Data Binding

In Lightning web components, the data binding for property values is one way. If the property value changes in the owner component, the updated value propagates to the child component. The reverse is not true.

Let’s see a simple data-binding example.

<!-- owner.html -->
<template>
    <c-child first-name={fName}></c-child>
</template>

The data-binding behavior is intentionally simpler and more predictable in Lightning web components. The child component must treat any property values passed from the owner component as read-only. If the child component changes a value passed from an owner component, you see an error in the browser console.

To trigger a mutation for the property value provided by the owner component, the child component can send an event to the parent. If the parent owns the data, the parent can change the property value, which propagates down to the child component via the one-way data binding.

Resources

Keep learning for
free!
Sign up for an account to continue.
What’s in it for you?
  • Get personalized recommendations for your career goals
  • Practice your skills with hands-on challenges and quizzes
  • Track and share your progress with employers
  • Connect to mentorship and career opportunities