---
description: React-specific patterns and Salesforce data access for SFDX web apps
paths:
  - "**/webapplications/**/*"
---

# React Web App (SFDX)

React-specific guidelines for data access, component library, and Salesforce integration.

For layout, navigation, and generation rules, see **webapp.md**.

## Project Structure

- Web app root: `force-app/main/default/webapplications/<appName>/`
- Entry: `src/app.tsx`
- Layout shell: `src/appLayout.tsx`
- Dev server: `npm run dev`
- Build: `npm run build`

## Routing (React Router)

Use a **single** router package for the app. When using `createBrowserRouter` and `RouterProvider` from `react-router`, all routing imports must come from **`react-router`** — not from `react-router-dom`.

- ✅ `import { createBrowserRouter, RouterProvider, Link, useNavigate, useLocation, Outlet } from 'react-router';`
- ❌ `import { Link } from 'react-router-dom';` when the app uses `RouterProvider` from `react-router`

Mixing packages causes "Cannot destructure property 'basename' of ... as it is null" because `Link`/hooks from `react-router-dom` read a different context than the one provided by `RouterProvider` from `react-router`.

## Component Library (MANDATORY)

Use **shadcn/ui** for UI components:

```typescript
import { Button } from '@/components/ui/button';
import { Card, CardHeader, CardContent } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
```

## Icons & No Emojis (MANDATORY)

- **Icons:** Use **lucide-react** only. Do not use Heroicons, Simple Icons, or other icon libraries in the web app.
  - ✅ `import { Menu, Settings, Bell } from 'lucide-react';` then `<Menu className="w-5 h-5" />`
  - ❌ Any emoji character as an icon or visual element (e.g. 🎨 🚀 ⚙️ 🔔)
  - ❌ Other icon libraries (e.g. `@heroicons/react`, `react-icons`, custom SVG icon sets)
- **No emojis ever:** Do not use emojis anywhere in the app: no emojis in UI labels, buttons, headings, empty states, tooltips, or any user-facing text. Use words and lucide-react icons instead.
  - ✅ "Settings" with `<Settings />` icon
  - ❌ "⚙️ Settings" or "Settings 🔧"
- For icon usage patterns and naming, see the **webapp-ui-ux** skill (`.a4drules/skills/webapp-ui-ux/`, especially `data/icons.csv`).

## Editing UI — Source Files Only (CRITICAL)

When asked to edit the home page, a section, or any on-screen UI:

- **Always edit the actual React/TSX source files.** Never output, paste, or generate raw HTML. The UI is built from components; the agent must locate the component file(s) that render the target area and change **JSX in those files**.
- **Do not** replace JSX with HTML strings, and do not copy browser-rendered DOM (e.g. expanded `<div>`, `data-slot`, long class strings) into the codebase. That is the output of React + shadcn; the source is `.tsx` with `<Card>`, `<Input>`, etc.
- **Edit inside the component that owns the UI.** If the element to change is rendered by a **child component** (e.g. `<GlobalSearchInput />` in `Home.tsx`), make the edit **inside that component's file** (e.g. `GlobalSearchInput.tsx`). Do **not** only wrap the component with extra elements in the parent (e.g. adding a `<section>` or `<div>` around `<GlobalSearchInput />`). The parent composes components; the visual/content change belongs in the component that actually renders that part of the UI.
- **Where to edit:**
  - **Home page content:** `src/pages/Home.tsx` — but if the target is inside a child (e.g. `<GlobalSearchInput />`), edit the child's file, not the parent.
  - **Layout, header, nav:** `src/appLayout.tsx`.
  - **Feature-specific UI (e.g. search, list):** open the **specific component file** that renders the target (e.g. `GlobalSearchInput.tsx` for the search input UI).
- **Steps:** (1) Identify which **component** owns the section (follow the import: e.g. `GlobalSearchInput` → its file). (2) Open that component's file and modify the JSX there. (3) Only change the parent (e.g. `Home.tsx`) if the change is layout/ordering of components, not the internals of a child. Do not emit a standalone HTML snippet as the “edit.”

## Mobile Nav / Hamburger — Must Be Functional

When adding a navigation menu that includes a hamburger (or Menu icon) for mobile:

- **Do not** add a hamburger/Menu icon that does nothing. The icon must **toggle a visible mobile menu**.
- **Required implementation:**
  1. **State:** e.g. `const [isOpen, setIsOpen] = useState(false);`
  2. **Toggle button:** The hamburger/Menu button must have `onClick={() => setIsOpen(!isOpen)}` and `aria-label="Toggle menu"` (or equivalent).
  3. **Conditional panel:** A mobile-only menu panel that renders when `isOpen` is true, e.g. `{isOpen && ( <div className="..."> ... nav links ... </div> )}`. Use responsive classes (e.g. `md:hidden`) so the panel is shown only on small screens if the desktop nav is separate.
  4. **Close on navigation:** Each nav link in the mobile panel should call `onClick={() => setIsOpen(false)}` (or similar) so the menu closes when the user navigates.
- Implement this in `appLayout.tsx` (or the component that owns the header/nav). See existing apps for the full pattern (state + button + conditional panel + link onClick).

## Styling

- Use **Tailwind CSS** utility classes
- Follow consistent spacing, color, and typography conventions

## Module & Platform Restrictions

React apps must NOT import Salesforce platform modules:

- ❌ `@salesforce/*` (except `@salesforce/sdk-data`)
- ❌ `lightning/*`
- ❌ `@wire` (LWC-only)

Use standard web APIs and npm packages only.

## Data Access (CRITICAL)

All Salesforce API calls **must** go through the DataSDK (`@salesforce/sdk-data`). Do NOT use `axios` or raw `fetch` — the SDK handles authentication and CSRF.

### GraphQL (Preferred)

For queries and mutations, follow the **`graphql-data-access`** skill in `feature-graphql`. It covers schema exploration, query patterns, codegen, type generation, and guardrails.

### UI API (Fallback)

When GraphQL cannot cover the use case, use `sdk.fetch!()` for UI API endpoints:

```typescript
const sdk = await getDataSDK();
const resp = await sdk.fetch!('/services/data/v62.0/ui-api/records/{recordId}');
```

### Apex REST is NOT Available

Apex REST cannot be called from React applications. If Apex seems required:
1. Evaluate if GraphQL can accomplish the task
2. If not, inform the user the feature is not supported in React

### MCP Tool Integration

Before implementing data access:
1. Check if `orchestrate_lds_data_requirements` tool is available
2. Use it to get guidance on the appropriate pattern (GraphQL or UI API)
3. If it recommends Apex REST, ignore and use GraphQL instead

## Einstein LLM Gateway (AI Features)

```typescript
import { getDataSDK } from '@salesforce/sdk-data';

async function callEinsteinGenerations(prompt: string): Promise<string> {
  const sdk = await getDataSDK();
  const resp = await sdk.fetch!('/services/data/v62.0/einstein/llm/prompt/generations', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      additionalConfig: { applicationName: 'PromptTemplateGenerationsInvocable' },
      promptTextorId: prompt,
    }),
  });

  if (!resp.ok) throw new Error(`Einstein LLM failed (${resp.status})`);
  const data = await resp.json();
  return data?.generations?.[0]?.text || '';
}
```

## Error Handling

```typescript
async function safeFetch<T>(fn: () => Promise<T>): Promise<T> {
  try {
    return await fn();
  } catch (err) {
    console.error('API Error:', err);
    throw err;
  }
}
```

### Authentication Errors

On 401/403 response, trigger page refresh to redirect to login:
```typescript
window.location.reload();
```

## Security Standards

- Validate user permissions before data operations
- Respect record sharing rules and field-level security
- Never hardcode credentials or secrets
- Sanitize all user inputs
- Use HTTPS for all API calls

## Performance

- Use React Query or RTK Query for caching API data
- Use `React.memo`, `useMemo`, `useCallback` where appropriate
- Implement loading and error states

## Anti-Patterns (FORBIDDEN)

- ❌ Calling Apex REST from React
- ❌ Using `axios` or raw `fetch` for Salesforce APIs
- ❌ Direct DOM manipulation in React
- ❌ Using LWC patterns (`@wire`, LDS) in React
- ❌ Hardcoded Salesforce IDs or URLs
- ❌ Missing error handling for async operations
- ❌ Mixing `react-router` and `react-router-dom` (e.g. `RouterProvider` from `react-router` but `Link` from `react-router-dom`) — use one package for all routing imports
- ❌ Using emojis anywhere in the app (labels, icons, empty states, headings, tooltips)
- ❌ Using any icon library other than **lucide-react** (no Heroicons, react-icons, or emoji-as-icon)
- ❌ Outputting or pasting raw HTML when editing UI — always edit the React/TSX source files (e.g. `Home.tsx`, `appLayout.tsx`, feature components)
- ❌ Adding a hamburger or Menu icon for mobile nav without implementing toggle state, conditional menu panel, and close-on-navigate

## Quality Checklist

- [ ] Entry point maintained (`app.tsx`)
- [ ] Uses shadcn/ui and Tailwind
- [ ] Icons from **lucide-react** only; no emojis anywhere in UI or user-facing text
- [ ] UI changes made by editing .tsx source files (never by pasting raw HTML)
- [ ] If mobile hamburger exists: it has toggle state, conditional menu panel, and close on link click
- [ ] All routing imports from same package as router (e.g. `react-router` only)
- [ ] DataSDK used for all Salesforce API calls
- [ ] Proper error handling with try/catch
- [ ] Loading and error states implemented
- [ ] No hardcoded credentials or IDs
