Skip to main content

Migrate Markup and CSS

Learning Objectives

After completing this unit, you’ll be able to:
  • Migrate HTML and markup from Aura components to Lightning web components.
  • Migrate CSS from Aura components to Lightning web components.

The DreamHouse App

The best way to compare the Aura Component and Lightning Web Components programming models is to look at code for similar components written in the two models.

We’re going to look at some components that are part of the DreamHouse app, which is a relatively complex app that Salesforce has developed to showcase Lightning components. DreamHouse is a sample application for a real-estate business built on the Salesforce platform. The app enables brokers to manage their properties, and enables buyers to find their dream home.

The DreamHouse app has separate implementations for Aura components and Lightning web components, and the full code for each is available in two different GitHub repos. The functionality isn’t identical in both versions of the app, but there are a few components that are useful for comparing the two programming models.

The code for the two implementations of the DreamHouse app is available in GitHub repos.

We look at snippets of code from Aura components and show you how the Aura code maps to Lightning web component code. We also show you how Aura component concepts translate to Lightning web components, and we focus on the core snippets to illustrate these concepts. Then you can explore the components if you’re interested in more context.

Component Bundles

The component bundle file structure is different for an Aura component and a Lightning web component. Here’s how the files map between the two types of component.

Resource Aura File Lightning Web Components File
Markup sample.cmp sample.html
Controller sampleController.js sample.js
Helper sampleHelper.js
Renderer sampleRenderer.js
CSS sample.css sample.css
Documentation sample.auradoc Not currently available
Design sample.design sample.js-meta.xml
SVG sample.svg Include in HTML file or upload as a static resource
Note

The separate controller, helper, and renderer files in an Aura component map to one JavaScript file in a Lightning web component.

A Lightning web component must include an HTML file, a JavaScript file, and a configuration file. It can optionally include a CSS file and more JavaScript files.

A component that doesn’t render any UI, also known as a service component or library, must include a JavaScript file and a metadata configuration file.

In this unit, we look at the HTML and CSS files. In the next unit, we explore the JavaScript file.

Lightning web components also have a configuration file, which defines the metadata values for the component, including the design configuration for components intended for use in Lightning App Builder. The configuration file houses some of the metadata that is defined by interfaces, such as flexipage:availableForRecordHome, in Aura components.

Migrate Markup

An Aura component contains markup in a .cmp file. The file starts with an <aura:component> tag and can contain HTML and Aura-specific tags.

A Lightning web component contains markup in a .html file. The file starts with a <template> tag and can contain HTML and directives for dynamic content.

Let’s see how the Aura markup maps to content in the HTML and JavaScript files of a Lightning web component.

Attributes Become JavaScript Properties

Migrate attributes from <aura:attribute> tags in the markup (.cmp) of an Aura component to JavaScript properties in the JavaScript file (.js) of a Lightning web component.

Let’s look at these attributes in the markup for the PropertySummary Aura component. You can see the complete file in the DreamHouse Aura component GitHub repo.

    <aura:attribute name="recordId" type="Id" />
    <aura:attribute name="property" type="Property__c" />

In a Lightning web component, we use JavaScript properties instead of attributes in markup. Let’s look at the properties in propertySummary.js.

import { LightningElement, api } from 'lwc';
export default class PropertySummary extends LightningElement {
    @api recordId;
    property;
        ...
}

The recordId and property attributes in the Aura component become the recordId and property JavaScript properties in the Lightning web component.

The @api decorator marks recordId as a public property. A public property is part of the public API for the component, which means that it can be set in Lightning App Builder, or by a parent component that uses the component in its markup.

Basic Aura Expressions Become HTML Expressions

Migrate basic expressions from markup in an Aura component to expressions in HTML in a Lightning web component. An example of a basic expression is a reference to an attribute in an Aura component.

For example, the PropertyPaginator Aura component uses basic expressions to display the values of the page, pages, and total attributes.

<aura:component >
    <aura:attribute name="page" type="integer"/>
    <aura:attribute name="pages" type="integer"/>
    <aura:attribute name="total" type="integer"/>
    <div class="centered">{!v.total} properties • page {!v.page} of {!v.pages}</div>
</aura:component>

Here’s the equivalent syntax in the HTML file of the paginator Lightning web component.

<template>
    {totalItemCount} items • page {currentPageNumber} of {totalPages}
</template>
Note

Dynamic content in a Lightning web component’s HTML file doesn’t have an exclamation point or value provider (v.) syntax. Don’t use the expression syntax from Aura components in a Lightning web component even though your fingers might be used to typing it!

The HTML references the totalItemCount property in paginator.js. The {currentPageNumber} and {totalPages} expressions reference getters that process the pageNumber and pageSize properties.

import { LightningElement, api } from 'lwc';
export default class Paginator extends LightningElement {
    /** The current page number. */
    @api pageNumber;
    /** The number of items on a page. */
    @api pageSize;
    /** The total number of items in the list. */
    @api totalItemCount;
    get currentPageNumber() {
        return this.totalItemCount === 0 ? 0 : this.pageNumber;
    }
    get totalPages() {
        return Math.ceil(this.totalItemCount / this.pageSize);
    }
}

We tackle more complex expressions soon after we understand conditional statements.

Aura Conditionals Become HTML Conditionals

Migrate <aura:if> tags in an Aura component to lwc:if, lwc:elseif, or lwc:else directives in a Lightning web component’s HTML file.

Here’s some conditional markup in the BrokerDetails Aura component.

<aura:if isTrue="{!v.property.Broker__c}">
    <lightning:recordForm recordId="{!v.property.Broker__c}"
      objectApiName="Broker__c"
      fields="{!v.brokerFields}" columns="2"/>
</aura:if>

Here’s similar HTML in the brokerCard Lightning web component.

<template lwc:if={property.data}>
    <lightning-record-form object-api-name="Broker__c" 
      record-id={brokerId} fields={brokerFields} 
      columns="2">
    </lightning-record-form>
</template>
Note

Dynamic content in a Lightning web component’s HTML file doesn’t have quotes around the {brokerId} reference. It’s not a typo!

The HTML file of a Lightning web component starts with the standard HTML <template> tag, and it can also contain other <template> tags in its body. In this example, the content in the <template> tag conditionally renders depending on the result of the lwc:if directive.

Complex Aura Expressions Become JavaScript Logic

We saw how basic Aura expressions, such as references to attribute values, become HTML expressions. Migrate a complex Aura expression, such as a comparison operation or a ternary operator, in an Aura component to a JavaScript getter in a Lightning web component.

Aura components support a rich set of expressions, such as this example in the PropertyPaginator Aura component, which uses {!v.page > 1}.

<aura:if isTrue="{!v.page > 1}">
    <lightning:buttonIcon iconName="utility:left" variant="border" onclick="{!c.previousPage}"/>
</aura:if>

The expression language in Aura allows you to include more logic in your markup. The downside is that it’s harder to unit test this logic, and the functionality is only a subset of what you can do in JavaScript.

In a Lightning web component, move the complex expression into JavaScript. Now the code can be unit tested, which makes for more stable code and happier developers. You also have access to the full capabilities of JavaScript to write your logic.

Here’s similar HTML in the paginator Lightning web component.

<template lwc:if={isNotFirstPage}>
    <lightning-button-icon icon-name="utility:chevronleft" onclick={previousHandler}></lightning-button-icon>
</template>

The getter in paginator.js evaluates the {isNotFirstPage} condition. For example:

import { LightningElement, api } from 'lwc';
export default class Paginator extends LightningElement {
    /** The current page number. */
    @api pageNumber;
    get isNotFirstPage() {
        return this.pageNumber > 1;
    }
}

Aura Iterations Become HTML Iterations

Migrate <aura:iteration> tags in an Aura component to for:each directives in a Lightning web component’s HTML file.

Here’s the Aura syntax in PropertyTileList.cmp.

<aura:iteration items="{!v.properties}" var="property">
    <c:PropertyTile property="{#property}"/>
</aura:iteration>

Here’s similar HTML in the propertyTileList Lightning web component.

<template for:each={properties.data.records} for:item="property">
    <c-property-tile property={property} key={property.Id} 
      onselected={handlePropertySelected}></c-property-tile>
</template>

Aura Initializers Become Lifecycle Hooks

Replace an init event handler in an Aura component with the standard JavaScript connectedCallback() method in a Lightning web component. The connectedCallback() lifecycle hook fires when a component is inserted into the DOM. Lifecycle hooks are callback methods that let you run code at each stage of a component’s lifecycle.

We use the init event in an Aura component to initialize a component after component construction but before rendering.

Here’s an init event handler in the PropertyCarousel Aura component.

<aura:handler name="init" value="{!this}" action="{!c.onInit}" />

The onInit function in the Aura component’s controller performs any necessary initialization.

In a Lightning web component, use connectedCallback() instead in the component’s JavaScript file. Here’s an example in propertySummary.js.

export default class PropertySummary extends LightningElement {
    ...
    connectedCallback() {
        // initialize component
    }
}

Migrate Base Components

Remember how Lightning web component can’t contain Aura components? What happens if the Aura component you need to migrate contains built-in components that Salesforce supplies? Base Lightning components are building blocks that Salesforce provides in the lightning namespace, and they’re available in both Aura components and Lightning web components. Base Lightning components have a different syntax when you use them in the two programming models.

This Aura component uses the lightning:formattedNumber base component.

<aura:component>
    <lightning:formattedNumber value="5000" style="currency"
      currencyCode="USD" />
</aura:component>

To migrate this markup to a Lightning web component:

  • Change the colon that separates the namespace (lightning) and component name (formattedNumber) to a dash.
  • Change the camel-case component name (formattedNumber) to a dash-separated name (formatted-number).
  • Change the camel-case attribute name (currencyCode) to a dash-separated name (currency-code).

Here’s the equivalent Lightning web component.

<template>
    <lightning-formatted-number value="5000" style="currency"
      currency-code="USD">
    </lightning-formatted-number>
</template>
Note

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, the <lightning-formatted-number> component reference includes a separate closing </lightning-formatted-number> tag. This requirement is different than Aura components, where self-closing tags are allowed.

Aura CSS Becomes Standard CSS

Lightning web components use standard CSS syntax. Remove the proprietary THIS class that Aura components use.

Here’s a portion of the CSS file for the PropertyTile Aura component.

.THIS .lower-third {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    color: #FFF;
    background-color: rgba(0, 0, 0, .3);
    padding: 6px 8px;
}
.THIS .lower-third > p {
    padding: 0;
    margin: 0;
}
.THIS .truncate {
  width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

Similar CSS for the propertyTile Lightning web component uses standard CSS instead. The THIS keyword is specific to Aura and isn’t used in Lightning web components.

.lower-third {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    color: #FFF;
    background-color: rgba(0, 0, 0, .3);
    padding: 6px 8px;
}
.lower-third > p {
    padding: 0;
    margin: 0;
}
.truncate {
  width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

Style Components with Lightning Design System

Custom components that live in Lightning Experience or in the Salesforce mobile application can use Lightning Design System without any import statements or static resources. Assign a Lightning Design System CSS class to an HTML element, and you’re good to go.

Lightning Web Components Inherit Aura Component Styling

Aura component styles cascade to any Lightning web components that they contain.

However, the styles of a Lightning web component never interfere with other Lightning web components or with Aura components. Yes, you read that correctly. Lightning web component styles don’t cascade to their children. Overriding styles that you don’t own can create many problems, so Lightning web components don’t allow it.

CSS Encapsulation with Shadow DOM

Lightning web components use a web-standard mechanism called shadow DOM that hides the elements inside the component from the page that contains the component. Because Lightning web components have a shadow DOM, styles defined in a component’s style sheet are scoped to the component. They don’t apply to parent, child, or sibling components. This rule is strict, but it allows a component to be reused in different contexts without losing its styling. It also prevents a component’s styles from overriding styles in other parts of a page.

To make development easier for component authors, a Lightning web component’s shadow DOM works a bit differently than the web standard. One difference is that the shadow DOM for a Lightning web component is created automatically. The component author doesn’t have to implement it. Also, a Lightning web component’s shadow DOM works even on browsers that don’t natively support shadow DOM.

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