Enterprise-grade React 19 Single Page Application embedded seamlessly into Salesforce Experience Cloud (LWR / Aura) with dual-channel GraphQL UI API & Apex Bridge integration.
This repository delivers an enterprise Customer Portal built with React 19, TypeScript, and Tailwind CSS v4, embedded within Salesforce Experience Cloud. It demonstrates how to combine the velocity and component ecosystem of modern front-end engineering with the security, scalability, and relational data model of the Salesforce Platform.
- Zero CORS / Iframe Cookie Friction: Features an infallible Apex PostMessage Bridge connecting the React application to the parent Lightning Web Component (
customerPortalHost), completely eliminating cross-origin, third-party cookie, and partitioned-cookie issues. - Dual-Channel Data Layer: Intelligently switches between Salesforce GraphQL UI API for reactive read operations and Apex
@AuraEnabled/@RestResourcecontrollers for transactional DML. - Dual Packaging Strategy: Ready for deployment via Salesforce UI Bundles (
@salesforce/vite-plugin-ui-bundle) or encapsulated as a zero-dependency Static Resource bundle (CustomerPortalHtml.resource). - Synchronized Routing: Bidirectional synchronization between React Router hash navigation (
#/support-tickets,#/billing-invoices, etc.) and Experience Cloud URL history viapushStateandpopstatelisteners. - Dynamic Security Context: Automatically resolves customer account and contact identity for authenticated community users, with automatic fallback for internal preview and demo modes.
| Module | Description | Salesforce Backing Objects |
|---|---|---|
| Executive Dashboard | Real-time aggregate KPIs (Open Tickets, Invoices Pending, CSAT Score, Active Subscriptions), recent activity feed, and dedicated account executive contact badge. | Account, Case, Invoice__c, Subscription__c, Portal_Activity__c |
| Support Tickets | Case management dashboard with status filters, search, modal for creating new tickets, and real-time threaded discussions (CaseComment). |
Case, CaseComment, Contact |
| Billing & Invoices | Financial overview, payment history, downloadable statement links, instant one-click invoice payment/settlement actions, and active payment method details. | Invoice__c, Account |
| Services & Subscriptions | Active infrastructure subscriptions with real-time usage meters (vCPU, GB storage, edge requests) against plan quotas and renewal schedules. | Subscription__c, Account |
| Team Management | Organization member directory, role assignment (Technical Lead, Billing Admin), new member invite workflow, and profile modifications. |
Contact, Account |
| Knowledge Base / FAQ | Searchable accordion FAQs categorized across Technical, Billing, Security, and General topics. | Portal_FAQ__c |
| Account & Profile | Company information, customer tier badge, personal contact info editor, timezone selector, and granular notification preferences. | Contact, Account |
flowchart TD
subgraph ClientTier["Client Browser"]
User["Customer / User"] -->|Visits Experience Site| ExpSite["Salesforce Experience Cloud (LWR / Aura)"]
ExpSite --> LWCHost["LWC Container: customerPortalHost"]
LWCHost -->|Embeds via Sandboxed Iframe / UI Bundle| ReactApp["React 19 Customer Portal SPA"]
subgraph ReactModules["React Portal Internal Architecture"]
Router["React Router v8 (Hash Routing)"]
StateMgr["useAsyncData Hook / Reactive State"]
PortalSvc["portalService.ts (Data Orchestration)"]
ApexCli["apexClient.ts (Bridge & REST)"]
GraphQLCli["graphqlClient.ts (Platform SDK / Direct)"]
Router --> StateMgr
StateMgr --> PortalSvc
PortalSvc --> ApexCli
PortalSvc --> GraphQLCli
end
end
subgraph SalesforcePlatform["Salesforce Core Platform"]
subgraph Gateways["API Gateways & Runtimes"]
GraphQLGateway["GraphQL UI API Gateway (/services/data/v63.0/graphql)"]
ApexWireBridge["LWC @AuraEnabled Controller Bridge"]
ApexRestDirect["Apex @RestResource Handlers (/services/apexrest/portal/*)"]
end
subgraph Controllers["Apex Controller Layer"]
SecCtx["CustomerPortalSecurityContext"]
CaseController["CustomerPortalCaseController"]
BillingController["CustomerPortalBillingController"]
TeamController["CustomerPortalTeamController"]
ProfileController["CustomerPortalProfileController"]
ActivityController["CustomerPortalActivityController"]
end
subgraph Database["Salesforce Multi-Tenant Data Store"]
StdDb[("Standard Objects: Account, Contact, Case, CaseComment")]
CstDb[("Custom Objects: Invoice__c, Subscription__c, Portal_FAQ__c, Portal_Activity__c")]
end
end
GraphQLCli -->|1. GraphQL Queries| GraphQLGateway
ApexCli <-->|2. window.postMessage Protocol| LWCHost
LWCHost <-->|Aura Wire / Imperative Calls| ApexWireBridge
ApexCli -.->|3. Fallback REST Requests| ApexRestDirect
GraphQLGateway --> StdDb & CstDb
ApexWireBridge --> SecCtx
ApexRestDirect --> SecCtx
SecCtx --> CaseController & BillingController & TeamController & ProfileController & ActivityController
CaseController & BillingController & TeamController & ProfileController & ActivityController --> StdDb & CstDb
The portal queries read data using the Salesforce GraphQL UI API. Queries are typed at compile-time with TypeScript schemas generated from Salesforce metadata.
sequenceDiagram
participant UI as React Component
participant Service as portalService
participant Client as graphqlClient
participant SF as Salesforce GraphQL API
UI->>Service: getDashboardSummary()
Service->>Client: executeGraphQL(GET_DASHBOARD_SUMMARY_QUERY, { accountId })
Client->>SF: POST /services/data/v63.0/graphql
SF-->>Client: { data: { uiapi: { query: { ... } } } }
Client-->>Service: Structured GraphQL Response
Service-->>UI: Normalized DashboardSummary Model
To ensure operations succeed without third-party cookie restrictions or CORS challenges, mutating actions (creating cases, adding comments, paying invoices, updating profiles) invoke the Apex Bridge:
sequenceDiagram
participant React as React (apexClient)
participant LWC as customerPortalHost (LWC)
participant Apex as Apex Controller (@AuraEnabled)
participant SF as Salesforce Database
React->>React: Generate messageId (msg_xxx_timestamp)
React->>LWC: window.parent.postMessage({ type: 'APEX_BRIDGE_REQ', action: 'createCase', params })
LWC->>Apex: await createCaseAura(params)
Apex->>SF: INSERT Case & CaseComment
SF-->>Apex: Inserted Record IDs
Apex-->>LWC: Case record object
LWC->>React: window.postMessage({ type: 'APEX_BRIDGE_RESP', messageId, success: true, data })
React-->>React: Resolve Promise & update UI state
.
βββ config/
β βββ project-scratch-def.json # Scratch org definition with standard features
βββ docs/
β βββ ARCHITECTURE.md # In-depth architectural specification
β βββ INTEGRATION_GUIDE.md # Guide to GraphQL, Apex bridge, and extending the app
βββ force-app/main/default/
β βββ classes/ # Apex controllers and test suites
β β βββ CustomerPortalActivityController.cls
β β βββ CustomerPortalBillingController.cls
β β βββ CustomerPortalCaseController.cls
β β βββ CustomerPortalProfileController.cls
β β βββ CustomerPortalSecurityContext.cls
β β βββ CustomerPortalTeamController.cls
β β βββ *Test.cls
β βββ corsWhitelistOrigins/ # Allowed origins for site communication
β βββ digitalExperienceConfigs/ # Digital Experience site configurations
β βββ digitalExperiences/ # Experience Cloud site bundles
β βββ lwc/
β β βββ customerPortalHost/ # Primary LWC host container with PostMessage bridge
β β βββ nextjsPortalHost/ # Alternate Next.js container host
β β βββ timesheetContainer/ # Embedded timesheet container component
β βββ objects/ # Custom object definitions and fields
β β βββ Account/ # Custom fields (CSAT_Score__c, Tier__c)
β β βββ Contact/ # Custom fields (Timezone, notification flags)
β β βββ Invoice__c/ # Invoicing custom object
β β βββ Portal_Activity__c/ # Activity timeline custom object
β β βββ Portal_FAQ__c/ # FAQ custom object
β β βββ Subscription__c/ # Subscriptions & usage tracking
β βββ permissionsets/
β β βββ Customer_Portal_Access.permissionset-meta.xml
β βββ sites/ # Experience Cloud site definition
β βββ staticresources/
β β βββ CustomerPortalHtml.resource # Inlined standalone production React bundle
β β βββ CustomerPortalHtml.resource-meta.xml
β βββ uiBundles/
β βββ ReactPortal/ # Full React 19 SPA Source Code
β βββ src/
β β βββ api/ # GraphQL & Apex clients, queries, generated types
β β βββ components/ # UI components (Radix / shadcn, badges, modals)
β β βββ hooks/ # Custom React hooks (useAsyncData, usePortalNavigate)
β β βββ pages/ # Portal views (Dashboard, Tickets, Billing, Team, etc.)
β β βββ services/ # Business logic, mock fallbacks, and models
β β βββ styles/ # Tailwind CSS v4 styling
β β βββ app.tsx # Application entry point
β β βββ appLayout.tsx # Main shell, sidebar, and top navigation
β β βββ routes.tsx # Hash router definitions
β βββ inline-bundle.js # Inliner script generating static resource
β βββ package.json # React dependencies and scripts
β βββ vite.config.ts # Vite configuration with Tailwind v4 & Salesforce plugins
βββ scripts/
β βββ apex/
β β βββ add_cors.apex # Script to whitelist site domains
β β βββ grant_guest_access.apex # Utility to grant guest permissions
β β βββ seed_portal_data.apex # Comprehensive sample data seeder
β βββ check_resource_headers.js # Verification script for static resource headers
β βββ check_site_html.js # Site availability check script
βββ sfdx-project.json # Salesforce DX project manifest (API v67.0)
βββ package.json # Root project scripts (build, lint, test)
- Node.js:
v22.0.0or higher - npm:
v10.0.0or higher - Salesforce CLI (
sf):v2.0.0or higher (Install Guide) - Salesforce Org: Developer Edition or Scratch Org with Experience Cloud enabled
# Clone the repository
git clone https://github.com/nandini-teqfocus/sf-react-exp-cloud.git
cd sf-react-exp-cloud
# Install root dependencies
npm install
# Install React application dependencies
cd force-app/main/default/uiBundles/ReactPortal
npm install
cd ../../../..You can develop and test the React frontend locally with mock data and hot reloading:
# From the project root
npm run dev
# Or directly from the ReactPortal folder
cd force-app/main/default/uiBundles/ReactPortal
npm run devOpen http://localhost:5173 in your browser.
# Authorize your Developer Edition or Sandbox org
sf org login web -a MyPortalOrg
# Or set as default target org
sf config set target-org=MyPortalOrgCompile the React application and build the inlined static resource:
# Build React bundle via Vite & compile TypeScript
npm run build:react
# Generate standalone bundle and update CustomerPortalHtml.resource
cd force-app/main/default/uiBundles/ReactPortal
node inline-bundle.js
cd ../../../..Deploy all Apex classes, custom objects, permission sets, static resources, and LWC components to your org:
sf project deploy start --target-org MyPortalOrgAssign the Customer_Portal_Access permission set to your user or Experience Cloud community profile:
sf org assign permset -n Customer_Portal_Access --target-org MyPortalOrgRun the Apex seeding script to create test accounts, contacts, cases, invoices, subscriptions, FAQs, and activities:
sf apex run --file scripts/apex/seed_portal_data.apex --target-org MyPortalOrg- In Salesforce Setup, go to Digital Experiences > All Sites.
- Open Experience Builder for your site (or create a new LWR / Aura Customer Portal site).
- Drag the
customerPortalHostLightning Web Component onto the site page canvas. - Set the page layout to full-width without header/footer margins.
- Click Publish to make the portal live.
The React portal includes comprehensive unit tests verifying the service layer, mock data fallbacks, and Apex client behaviors:
cd force-app/main/default/uiBundles/ReactPortal
npm testRun Jest tests for Lightning Web Components:
npm run test:unitExecute Apex test classes with code coverage:
sf apex run test --code-coverage --result-format human --target-org MyPortalOrg- Zero Hardcoded Credentials: No passwords, OAuth client secrets, or private tokens exist in source code or version control.
- Dynamic Security Context: Apex controllers use
CustomerPortalSecurityContextto strictly filter records by the logged-in user'sAccountIdandContactId. - Field & Object-Level Security (FLS): Access rules are governed by the
Customer_Portal_Accesspermission set. - Sandboxed Execution: React operates in an isolated iframe inside the LWC container, communicating only via controlled
postMessagecontracts.
- Description: Enterprise React 19 Customer Portal embedded in Salesforce Experience Cloud with GraphQL UI API, Apex Bridge, and Tailwind CSS v4.
- Topics:
salesforce,experience-cloud,react19,lwc,graphql,apex,vite,tailwindcss,typescript,ui-bundle,salesforce-dx
Distributed under the MIT License. See LICENSE for details.