React• 2026-06-15• 8 min read
How to Embed Modern React Applications Inside Salesforce LWC
Written by the SparrowLaunch engineering team
Lightning Web Components (LWC) are excellent for standard forms and record pages, but building highly interactive user flows—such as custom product configurators, interactive visual calendars, or drag-and-drop workflow interfaces—can quickly become a maintenance challenge.
An alternative approach is embedding a custom **React SPA (Single Page Application)** directly inside a container LWC. This allows you to leverage the full React ecosystem (state management, component libraries, visual canvas elements) while remaining securely hosted inside Salesforce.
### High-Level Architecture
The integration works by bundling your React application into a single JavaScript and CSS asset file using **Vite**, uploading that bundle as a **Salesforce Static Resource**, and mounting it inside a shadow DOM element in LWC.
1. **The Container LWC**: Serves as the DOM mount target and manages the OAuth or Session ID handshake.
2. **The Apex Controller**: Handles database queries. LWC passes Apex query promises down to the embedded React app.
3. **The LWC-React Bridge**: React uses standard browser CustomEvents to request data, and the parent LWC listens to these events and returns database results back.
### Configuring Vite for Salesforce Compatibility
Salesforce requires assets to load from single compiled files. In your React project, modify your `vite.config.ts` to disable file chunking:
```typescript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
rollupOptions: {
output: {
entryFileNames: 'bundle.js',
assetFileNames: 'bundle.[ext]',
chunkFileNames: 'chunk.js',
}
}
}
});
```
Once compiled, zip the `dist/` folder and upload it to Salesforce as a Static Resource named `ReactOpportunityBundler`.
### Communicating Between LWC and React
To query data, React sends custom events that bubble up to the LWC:
```javascript
// Inside React Component
const requestSalesforceData = (opportunityId) => {
const event = new CustomEvent('sf_query_data', {
detail: { opportunityId },
bubbles: true,
composed: true
});
window.dispatchEvent(event);
};
```
Inside the container LWC, register a listener in the `renderedCallback()`:
```javascript
// Inside LWC Controller
renderedCallback() {
if (this.reactLoaded) return;
this.reactLoaded = true;
this.template.addEventListener('sf_query_data', (event) => {
const oppId = event.detail.opportunityId;
// Call Apex Method
getOpportunityLineItems({ oppId })
.then(result => {
// Send data back to React window
this.sendDataToReact(result);
});
});
}
```
### Key Security & Styling Considerations
- **Shadow DOM Limits**: Salesforce wraps LWCs in a Shadow DOM. React's styling needs to be compiled directly into the JS bundle or loaded explicitly via LWC’s `loadStyle` function to prevent page styling collisions.
- **CSRF & Locker Service**: Ensure your React app respects Salesforce security rules. Do not perform direct external HTTP requests unless you whitelist the domain inside Salesforce CSP Settings.
Article Tags
#React#Salesforce LWC#Vite#Web Development