Claude
Skills
Sign in
Back

sales-app-extensibility

Included with Lifetime
$97 forever

Apply when building, customizing, or deploying extensions for VTEX Sales App. Covers the complete 7-step workflow from prerequisite checks through code generation to deployment, including extension points (cart, PDP, menu), React hooks (useCart, usePDP, useCartItem, useCurrentUser, useExtension), TypeScript types, secure API integration patterns, and API documentation ingestion (OpenAPI, URLs, or inline specs) to generate typed integrations.

Web Dev

What this skill does


# Sales App Extensibility

## When this skill applies

Use this skill when building, customizing, or deploying extensions for VTEX Sales App.

- Adding features to the cart page (promotions, loyalty, services)
- Adding features to the Product Detail Page (badges, recommendations, warranties)
- Adding features to the menu (user profile, navigation, metrics)
- Integrating external APIs into Sales App extensions
- Generating, scaffolding, or validating extension code for Sales App

Do not use this skill for:
- Regular FastStore storefront customization (use `faststore-storefront`)
- Building VTEX IO apps (use `vtex-io-*` skills)
- Sales App core development or framework modifications

### Prerequisite: FastStore project

The project root must contain: `biome.json`, `faststore.json`, `package.json`, `tsconfig.json`, `turbo.json`.

If any are missing, **STOP**. The user must install FastStore first:

```bash
npx @vtex/fsp-cli init
# Prompt: "What is the application name?" → enter name or press Enter for default
cd <application-name> && yarn
```

Documentation: https://beta.fast.store/getting-started

### Prerequisite: Sales App module

Inside the FastStore project, a Sales App workspace must exist (typically at `packages/sales-app`) with `src/`, `package.json`, and `tsconfig.json`. Check root `package.json` `"workspaces"` for a path containing `sales-app`. If missing, **STOP**:

```bash
yarn add @vtex/sales-app -D -W
npx fsp create
# Prompts: account name → "Sales App" → path (default or custom)
# Then add the path to root package.json "workspaces" array
yarn install
```

Documentation: https://beta.fast.store/sales-app/setting-up

**Do NOT proceed to discovery or code generation until both prerequisites are confirmed.**

## Decision rules

Follow the mandatory 7-step workflow (Steps 0–6) in order. Do not skip steps.

| Step | Purpose | Gate |
|------|---------|------|
| 0 | Check prerequisites | FastStore + Sales App installed → proceed; otherwise STOP |
| 1 | Discovery | Understand what the user wants to build |
| 2 | Requirements & Plan | Map requirements → generate plan → **wait for user approval** |
| 3 | Code Generation & Validation | Generate `Component.tsx` + `Component.css` (plain CSS, never `.module.css`) + `index.tsx` → validate |
| 4 | Documentation | Generate `docs/<ExtensionName>.md` explaining the extension |
| 5 | Local Testing | Provide dev commands and URLs |
| 6 | Build & Deploy | Build command → deployment guide |

### Extension point selection

| Extension Point | Category | Available Hooks | Layout Shift |
|----------------|----------|----------------|--------------|
| `cart.cart-list.after` | Cart | useCart, useExtension | No |
| `cart.cart-item.after` | Cart | useCart, useCartItem, useExtension | Yes |
| `cart.order-summary.after` | Cart | useCart, useExtension | Yes |
| `pdp.sidebar.before` | PDP | usePDP, useCart, useExtension | Yes |
| `pdp.sidebar.after` | PDP | usePDP, useCart, useExtension | Yes |
| `pdp.content.after` | PDP | usePDP, useCart, useExtension | Yes |
| `menu.item` | Menu | useExtension | No |
| `menu.drawer-content` | Menu | useCurrentUser, useExtension | No |

### Hook availability

- **useCart** → all cart + PDP extensions
- **useCartItem** → `cart.cart-item.after` only
- **useCurrentUser** → `menu.drawer-content` only
- **usePDP** → PDP extensions only
- **useExtension** → all extension points

### Template selection

- No API + no hooks → **simple template**
- No API + hooks → **hook template**
- API + no auth → **API template**
- API + VTEX IO proxy → **IO proxy template** (recommended)
- API + direct auth → **direct auth template** (insecure, warn user)
- API doc provided → generate TypeScript interfaces from extracted response shapes; no API doc → use `${DATA_INTERFACE}` placeholder

### API authentication strategy

1. **Recommended: VTEX IO Proxy App** — IO app stores keys server-side. Extension uses `credentials: 'include'` with a **relative path** (`/_v/my-api/data`).
2. **Insecure: Direct Auth** — Keys in frontend code, visible in browser. Testing/development only.

## Hard constraints

### Constraint: Component must return JSX.Element, never null

`defineExtensions` expects `ExtensionPointComponent` which returns `Element`, not `Element | null`.

**Why this matters**
Returning `null` causes a TypeScript compilation error. The build will fail.

**Detection**
`return null` in any component registered with `defineExtensions`.

**Correct**

```typescript
export function MyExtension(): JSX.Element {
  if (!data) return (<></>);
  return <div>{data.value}</div>;
}
```

**Wrong**

```typescript
export function MyExtension(): JSX.Element | null {
  if (!data) return null;
  return <div>{data.value}</div>;
}
```

### Constraint: Guard optional properties before use

`CartItem.manualPrice` (number | undefined), `CartItem.productRefId` (string | undefined), `CartItem.attachments` (Attachment[] | undefined) must be guarded.

**Why this matters**
TypeScript strict mode rejects accessing possibly-undefined values.

**Detection**
`item.manualPrice`, `item.productRefId`, or `item.attachments` without `?.`, `??`, `&&`, or `!= null`.

**Correct**

```typescript
const price = item.manualPrice ?? item.sellingPrice;
const refId = item.productRefId ?? 'N/A';
const count = item.attachments?.length ?? 0;
```

**Wrong**

```typescript
const price = item.manualPrice;
const refId = item.productRefId.toUpperCase();
const count = item.attachments.length;
```

### Constraint: useCartItem().item may be undefined

`item` from `useCartItem()` is `CartItem | undefined`.

**Why this matters**
Accessing properties on `undefined` causes a runtime crash.

**Detection**
Destructured `item` from `useCartItem()` used without `if (!item)` guard.

**Correct**

```typescript
const { item } = useCartItem();
if (!item) return (<></>);
return <div>{item.name}</div>;
```

**Wrong**

```typescript
const { item } = useCartItem();
return <div>{item.name}</div>;
```

### Constraint: defineExtensions is required in index.tsx

Entry point must use `defineExtensions` from `@vtex/sales-app`.

**Why this matters**
Without it, the build succeeds but no extensions render.

**Detection**
Missing `defineExtensions` import or call in `index.tsx`.

**Correct**

```typescript
import { defineExtensions } from '@vtex/sales-app';
import { MyExtension } from './components/MyExtension';
export default defineExtensions({ 'cart.cart-list.after': MyExtension });
```

**Wrong**

```typescript
import { MyExtension } from './components/MyExtension';
export default { 'cart.cart-list.after': MyExtension };
```

### Constraint: Extension point names must match exactly

IDs are a fixed set. Non-existent names silently fail.

**Why this matters**
The extension renders nowhere. No build error — invisible at runtime.

**Detection**
Any name not in: `cart.cart-list.after`, `cart.cart-item.after`, `cart.order-summary.after`, `pdp.sidebar.before`, `pdp.sidebar.after`, `pdp.content.after`, `menu.item`, `menu.drawer-content`.

**Correct**

```typescript
defineExtensions({ 'cart.cart-list.after': MyExtension });
```

**Wrong**

```typescript
defineExtensions({ 'cart.list.after': MyExtension });
```

### Constraint: Hooks must be used in compatible extension points

Each hook has an `available_in` restriction.

**Why this matters**
Hook context is only mounted for specific extension points. Using elsewhere throws runtime errors.

**Detection**
`useCartItem` in PDP/menu. `useCurrentUser` in cart. `usePDP` in menu/cart-list.

**Correct**

```typescript
// useCartItem in cart.cart-item.after ✓
defineExtensions({ 'cart.cart-item.after': ItemWarranty });
```

**Wrong**

```typescript
// useCartItem in pdp.sidebar.after ✗ — will fail at runtime
defineExtensions({ 'pdp.sidebar.after': ItemWarranty });
```

## Preferred pattern

### Workflow overview

**Step 0** — Check FastStore + Sales App prerequisites. STOP if missing.

**Step 1** — Discovery. Detect use case from keywords, ask follow-up 

Related in Web Dev