Skip to content

k-j-kim/lwc-react-interop

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LWC React Interop

Convert React applications into Lightning Web Components (LWC) for Salesforce deployment.

Features

  • React to LWC Conversion - Compile React apps into deployable LWC components
  • Multi-LWC Code Splitting - Automatically splits large bundles across multiple LWC components to bypass Salesforce's 1MB file size limit
  • UTF-8 Safe Encoding - Handles Unicode characters (i18n libraries) correctly with proper base64 encoding/decoding
  • Shadow DOM Support - React renders inside Shadow DOM for proper LWC encapsulation
  • CSS Compilation - Automatically bundles and injects CSS into the Shadow DOM

How It Works

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                     Salesforce Org                               │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                    Main LWC (heavyApp)                   │    │
│  │  - Imports chunk LWCs                                    │    │
│  │  - Concatenates base64 chunks                            │    │
│  │  - Decodes UTF-8 and executes via new Function()         │    │
│  │  - Renders React into Shadow DOM                         │    │
│  └─────────────────────────────────────────────────────────┘    │
│           │              │              │                        │
│           ▼              ▼              ▼                        │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐               │
│  │ Chunk0 LWC  │ │ Chunk1 LWC  │ │ Chunk2 LWC  │  ...          │
│  │ (800KB max) │ │ (800KB max) │ │ (800KB max) │               │
│  │ base64 data │ │ base64 data │ │ base64 data │               │
│  └─────────────┘ └─────────────┘ └─────────────┘               │
│                                                                  │
│  ┌─────────────┐ ┌─────────────┐                                │
│  │  react LWC  │ │ reactdom    │  (Shared React/ReactDOM)       │
│  │             │ │    LWC      │                                │
│  └─────────────┘ └─────────────┘                                │
└─────────────────────────────────────────────────────────────────┘

Multi-LWC Code Splitting

Salesforce enforces a 1MB limit per JavaScript file in LWC components. Large React apps with many dependencies (MUI, Recharts, i18next, etc.) can easily exceed this limit.

Solution: The generator automatically:

  1. Compiles the React app with webpack
  2. Base64 encodes the bundle (UTF-8 safe)
  3. Splits the encoded string into ~800KB chunks
  4. Creates a separate LWC component for each chunk (heavyAppChunk0, heavyAppChunk1, etc.)
  5. The main LWC imports all chunks, concatenates them, decodes, and executes
// Generated main LWC imports chunks from separate components
import { chunk as chunk0 } from 'c/heavyAppChunk0';
import { chunk as chunk1 } from 'c/heavyAppChunk1';
import { chunk as chunk2 } from 'c/heavyAppChunk2';

// Concatenate and decode (UTF-8 safe)
const encodedCode = chunk0 + chunk1 + chunk2;
const binaryStr = atob(encodedCode);
const bytes = new Uint8Array(binaryStr.length);
for (let i = 0; i < binaryStr.length; i++) {
  bytes[i] = binaryStr.charCodeAt(i);
}
const decodedCode = new TextDecoder('utf-8').decode(bytes);

// Execute
const scriptFunc = new Function('React', 'ReactDOM', decodedCode);
scriptFunc(React, ReactDOM);

UTF-8 Safe Encoding

Libraries like i18next include Unicode characters (Persian numerals, CJK characters, etc.). The browser's atob() function only handles Latin1 characters correctly.

Solution: We use TextDecoder('utf-8') to properly decode the base64 bytes as UTF-8:

  • Node.js: Buffer.from(code).toString('base64') - handles UTF-8
  • Browser: atob()Uint8ArrayTextDecoder('utf-8') - decodes UTF-8

Project Structure

lwc-react-interop/
├── src/
│   ├── App.js                 # Main React entry point
│   └── components/            # React components
├── scripts/
│   ├── lwcWrapperGenerator.js # Core generator with chunking logic
│   ├── generate-single.js     # CLI for single component generation
│   └── templates/             # LWC templates
├── dist/
│   └── lwc/
│       ├── heavyApp/          # Main wrapper LWC
│       ├── heavyAppChunk0/    # Chunk 0 LWC
│       ├── heavyAppChunk1/    # Chunk 1 LWC
│       ├── react/             # React library LWC
│       └── reactdom/          # ReactDOM library LWC
└── package.json

Quick Start

Local Development

Start the local development server to preview your React app:

yarn start

This starts a webpack dev server at http://localhost:3000 with hot module replacement.

Generate LWC Component

Generate a Lightning Web Component from your React app:

# Generate with custom component name
node scripts/generate-single.js --componentName heavyApp

# Or use the yarn script
yarn generate --componentName myApp

Deploy to Salesforce

# Deploy to default org
sf project deploy start --source-dir ./dist

# Deploy to specific org
sf project deploy start --source-dir ./dist --target-org myorg@example.com

Full Workflow

# 1. Generate the LWC
node scripts/generate-single.js --componentName heavyApp

# 2. Deploy to Salesforce
sf project deploy start --source-dir ./dist --target-org myorg

# Output shows chunk count:
# 📦 Splitting React bundle into 3 chunk(s) as separate LWC components
#   📦 Created chunk LWC: heavyAppChunk0
#   📦 Created chunk LWC: heavyAppChunk1
#   📦 Created chunk LWC: heavyAppChunk2
# ✅ Generated main wrapper and 3 chunk LWC component(s)

Configuration

Chunk Size

The default chunk size is 800KB (to stay well under the 1MB limit). To modify:

// In scripts/lwcWrapperGenerator.js
const chunkSize = 800000; // bytes

Adding Heavy Dependencies

The system handles large dependency bundles. Example tested dependencies:

  • @mui/material + @mui/icons-material (Material UI)
  • recharts (Charts)
  • i18next + react-i18next (Internationalization)
  • lodash, moment, date-fns (Utilities)
  • formik, react-hook-form, yup (Forms)
  • framer-motion (Animations)
  • @tanstack/react-table, @tanstack/react-query (Data)
  • antd (Ant Design)
  • chart.js (Charts)

Limitations

  1. Salesforce Locker Service - Some browser APIs may be restricted
  2. 1MB per file - Handled automatically via chunking
  3. ~39MB total package size - MDAPI deployment limit
  4. Shadow DOM - React renders in Shadow DOM; some libraries may need adaptation
  5. Dynamic imports - Not supported; all code must be bundled upfront

Troubleshooting

"Invalid or unexpected token" Error

If you see this error with new Function(), it's likely a UTF-8 encoding issue. Ensure the generator uses TextDecoder:

const binaryStr = atob(encodedCode);
const bytes = new Uint8Array(binaryStr.length);
for (let i = 0; i < binaryStr.length; i++) {
  bytes[i] = binaryStr.charCodeAt(i);
}
const decodedCode = new TextDecoder('utf-8').decode(bytes);

"Value too long for field: Source maximum length is 1000000"

The bundle exceeds 1MB. The generator should automatically chunk, but verify:

  1. Each chunk file is under 1MB
  2. All chunk LWCs are being deployed
  3. Check wc -c dist/lwc/*Chunk*/*.js

Component Not Rendering

Check browser console for errors:

  1. ReactAppComponent not found - Webpack didn't export correctly
  2. Container element not found - HTML template issue
  3. React errors - Check component code

License

ISC

About

Convert React applications into Lightning Web Components (LWC) for Salesforce deployment.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors