The Lightning Component Framework is a great UI framework for building modern components and applications on the Salesforce platform. If you are already invested in other UI frameworks, like React or Angular, you can also integrate components built with these frameworks inside Lightning Components.
In this post, I’ll share a project that demonstrates how to build a Lightning Component with React, or in other words, how to host a React Component inside a Lightning Component. The example used in this project is a simple Contact Search component fully implemented in React.
The React Component is loaded in your Salesforce Org as a Static Resource, and leverages the Lightning Component’s standard data access infrastructure.
There are two things a React component needs in order to function properly inside a Lightning Component:
- A reference to the DOM element that serves as the container for the React component
- A reference to a data service that the React component can call when it needs data
These two parameters are provided to the React Component in the Lightning Component’s reactInit() function that is executed when the Static Resource has been loaded (see the ReactSearch controller implementation below). The data service is just an object containing functions the React component can call when it needs data. These functions use the traditional Lightning Component syntax to access methods in the component’s Apex controller. The approach presented in this project also allows you to test your React components outside Salesforce using a mock data service.
Step 1: Building and Running the React Component Locally
- Clone the lightning-react repository which includes the React component and the associated build scripts
- Open a command prompt and navigate (cd) to the lightning-react directory
- Install the project dependencies:
npm install
- Build the project:
npm run webpack
- Test locally. The project includes a mock.js file that simulates a Lightning Component so that you can test your React code locally before deploying it in your Salesforce org. To test the app locally, open index.html in your browser and type a few characters in the search box.
Step 2: Hosting the React Component in a Lightning Component
- In the Developer Console, create an Apex Class named ContactController implemented as follows:
public with sharing class ContactController { @AuraEnabled public static List<Contact> findAll() { return [SELECT Id, Name from Contact limit 20]; } @AuraEnabled public static List<Contact> findByName(String name) { String key = '%' + name + '%'; return [SELECT Id, Name FROM Contact WHERE Name LIKE :key LIMIT 20]; } }
In a production environment, you should add code to the controller’s methods to enforce CRUD and FLS. More information here.
- Create a Static Resource named ReactSearch. Upload the search.bundle.js file available in the build directory.
- Install the Lightning Design System: On the downloads page, click v0.12.2 unmanaged package, then click the Install button. This will install the Lightning Design System as a static resource in your org.
You could also use your own styles, but using the Lightning Design System will ensure UI consistency.
- In the Developer Console, create a new Lightning Component (File > New > Lightning Component) named ReactSearch implemented as follows:
<aura:component controller="ContactController"> <ltng:require scripts="/resource/ReactSearch" styles="/resource/SLDS0122/assets/styles/salesforce-lightning-design-system-ltng.css" afterScriptsLoaded="{!c.reactInit}"/> <div aura:id="container"/> </aura:component>
- Click CONTROLLER (upper right corner) and implement the component’s controller as follows:
({ reactInit : function(component, event, helper) { var dataService = { findAll : $A.getCallback(function(callback) { var action = component.get("c.findAll"); action.setCallback(this, function(a) { var contacts = a.getReturnValue(); callback(contacts); }); $A.enqueueAction(action, false); }), findByName : $A.getCallback(function(name, callback) { var action = component.get("c.findByName"); action.setParams({name: name}); action.setCallback(this, function(a) { var contacts = a.getReturnValue(); callback(contacts); }); $A.enqueueAction(action, false); }) } var container = component.find("container").getElement(); reactSearch.init(container, dataService); } })
The key here is to wrap the data service’s functions in $A.getCallback() which returns a callback that is safe to invoke from outside the Lightning Component lifecycle.
- Create a new Lightning Application (File > New > Lightning Component) named ReactSearchApp implemented as follows:
<aura:application > <c:ReactSearch /> </aura:application>
- Click Preview (upper right corner) to run the app. Enter a few characters in the search box to search contacts by name.
Disclaimer
This project is meant as a technical proof of concept. It is not implied that the solution presented in this article would currently pass AppExchange security review. At the time of this writing, the security requirements checklist states that: “Any Lightning package that uses Angular or React or other third party DOM-based templating frameworks will automatically fail the security review until such time as we provide custom lightning components to properly encapsulate these frameworks. This is to avoid template injection attacks resulting from double interpolation. In order to use another framework as part of a package that uses Lightning, please use an iframe or Visualforce container until approved Lightning containers are published.”