How to Use React + Webpack for Front End Development in Salesforce Lightning

With the release of the Lightning Container Component Salesforce now has an excellent, secure, way of sharing data to from your org to your custom Javascript Code. Including React Apps. In this post we’ll explore how to set up React with Salesforce.

If you don’t want to go through an article and go straight to documentation here’s a boiler plate repo: react-lightning-boilerplate repo — a boilerplate project for developing React Components for use within Salesforce.

Overview Of The Tools/Frameworks

React

jeff 2.png

React is a component-based framework for building web user interfaces. It is a good idea to have a basic familiarity of React and JSX before working through this tutorial.

Lightning

jeff 3.png

Lightning is a component-based framework for building Salesforce User Interfaces.

Node and NPM

jeff 4.png

Node is a Javascript Engine that lets you run Javascript on your machine. NPM is short for “Node Package Manager” and is a tool to help you manage dependencies.

Webpack

jeff 5.png

Webpack is a tool that bundles Javascript together for production. Think of it as a file gatherer. We will use it in conjunction with Babel to transpile our Javascript to a stable older version that is generally well-supported across clients.

Babel

jeff 6.png

Babel is a Javascript compiler that we’re going to use to convert our Javascript to an older version that plays nicer with Salesforce.

What We’re Going to Build

A simple text rendering window that emulates the look of a a bash terminal. We will be able to send messages with lightning to our React Component.

jeff 7.png

The ‘How To’

Don’t worry, you don’t have to set up and configure all of those tools. I’ve already done that for you. We only have to set up the environment locally to get building.

Setup

  1. Fork this boilerplate repo: https://github.com/jefflombard/react-lightning-boilerplate
  2. Clone locally with git clone
  3. Follow the setup instructions.
  4. Spin up a scratch org and set it as your default using sfdx force:org:create -f ./config/project-scratch-def.json -s
  5. Run npm run deploy

Now it’s time to verify that everything is working.

  1. Run sfdx force:org:open
  2. Go to settings > developer console
  3. Open Lightning Resource > ReactDevContainerApp
jeff 8.png

Adding New Components

Alright so now we want to create a dumb component that will basically just output any text we give it.

  1. Create a new file in the src directory called TerminalScreen.js

2. Add the following code to the newly created TerminalScreen.js

// This line sets up react.
import React from 'react';
// This is an ES2015 Declaration of a function if it looks unfamilar to you, check out http://exploringjs.com/es6/
const TerminalScreen = props => (
    <div className="terminal-window">
        <p>{props.text}</p>
    </div>
);
// This line makes the variable TerminalScreen available to other files.
export default TerminalScreen;

3. Make the TerminalScreen component available to our app. Change the code in App.js to this:

import React, { Component } from 'react';
import LCC from 'lightning-container';
import './App.css';
// This Line Imports our Component That We Created in our other file.
import TerminalScreen from './TerminalScreen.js'
class App extends Component {
    
    constructor(props){
        super(props);
        this.state = {
            exampleMessageValue: "Hello from React!"
        }
    }
    
    componentDidMount(){
        LCC.addMessageHandler(this.messageRecievedHandler);
    }
    
    messageRecievedHandler(msg){
        const { name, value } = msg;
        
        console.log("Messaged received.");
        console.log(`Message name: ${name}`);
        console.log(`Message value: ${value}`);
        
        // Add Any Logic that should be handled here.
        
        switch (name) {
            case "example":
                console.log('Handle Example Messgage')
                break;
            default:
                console.log('Do Nothing');
        }
    }
    
    sendMessage(msg){
        // Message format should be an object like { name: "messageName", value: "message value"}
        LCC.sendMessage(msg);
    }
    
    sendMessageExample(){
        // You can wrap the send message function to easily send specific message types.
        
        this.sendMessage(
            {
                name: "example",
                value: this.state.exampleMessageValue
            }
        );
    }
    
    // The Render Functiion is what defines the markup of our component.
    render(){
        return (
            <div>
                <TerminalScreen text="test output"/>
            </div>
        );
    }
}
export default App;

4. Run npm run deploy and refresh your app window.

jeff 9.png

Styling The Component

Now that we have our component rendering on screen, it’s time to make it look like a terminal window.

  1. Create a new TerminalScreen.css file for our TerminalScreen component in the src directory.
.terminal-window {
    background-color: black;
    color: green;
    font-family: "Courier";
    height: 300px;
    padding: 10px;
    width: 400px;
}

2. Import the css into our React Component by updating our TerminalScreen.js file.

// This line sets up react.
import React from 'react';
// This adds the css file to our bundle
import './TerminalScreen.css';
// This is an ES2015 Declaration of a function if it looks unfamilar to you, check out http://exploringjs.com/es6/
const TerminalScreen = props => (
    <div className="terminal-window">
        <p>{props.text}</p>
    </div>
);
// This line makes the variable TerminalScreen available to other files.
export default TerminalScreen;

3. Clean up App.js and tell our terminal to output any received messages.

import React, { Component } from 'react';
import LCC from 'lightning-container';
import './App.css';
// This Line Imports our Component That We Created in our other file.
import TerminalScreen from './TerminalScreen.js'
class App extends Component {
    
    constructor(props){
        super(props);
        this.state = {
            message: ""
        }
    }
    
    componentDidMount(){
        LCC.addMessageHandler(this.messageRecievedHandler.bind(this));
    }
    
    messageRecievedHandler(msg){
        const { name, value } = msg;
        console.log("Messaged received.");
        console.log(`Message name: ${name}`);
        console.log(`Message value: ${value}`);
        
        // Add Any Logic that should be handled here.
        this.setState({
            message: value
        });
    }
    
    // The Render Functiion is what defines the markup of our component.
    render(){
        return (
            <div>
                <TerminalScreen text={this.state.message} />
            </div>
        );
    }
}
export default App;

4. Set Up Our Lightning Container to Send Messages to our React Component by editing react-lighting-boilerplate/force-app/main/default/aura/ReactContainer/ReactContainer.cmp

<aura:component implements="flexipage:availableForAllPageTypes" access="global" >
    <aura:attribute name="message" type="string" />
 
    <lightning:card title="Your React Component">
<lightning:container aura:id="jsApp" src="{!$Resource.ReactComponent + '/index.html'}"
            onmessage="{!c.handleMessage}"
            onerror="{!c.handleError}"/>
</lightning:card>
    <lightning:card title="Your Lightning Component">
        <p>Send Messages to Our ReactComponent</p>
        <lightning:input name="message" label="Enter some text" value="{!v.message}"/>
        <lightning:button variant="brand" label="Send" title="Send action" onclick="{! c.handleSendClick }" />
    </lightning:card>
    
</aura:component>

5. Set up our controller by editing react-lighting-boilerplate/force-app/main/default/aura/ReactContainer/ReactContainerController.js

({
    handleMessage: function (component, event, helper) {
        var message = event.getParams();
        component.set('v.message', message.payload.value);
    },
handleError: function (component, event, helper) {
        var error = event.getParams();
        console.log(error);
    },
    
    handleSendClick: function(component,event, helper) {
        var messageText = component.get('v.message');
        var message = {
            name: "Send To React",
            value: messageText
        };
        
        component.find('jsApp').message(message);
    }
})

6. Deploy with npm run deploy .

Finished example:

jeff 10.png

Further Reading

If you would like to see further code examples, or how to use other frameworks, check out Lightning Container Component: Building Components with React, Angular, and Other Libraries.

The branch with the code for this blog is here: https://github.com/jefflombard/react-lightning-boilerplate/tree/blog-example

We can help with Lightning Development

Our team at Eigen X has a great deal of experience with Lightning Development on the Salesforce Platform. Feel free to drop us a line: info@eigenx.com or jeffl@eigenx.com.