Storefront Next development — project setup, routing, data fetching, components, Page Designer, authentication, i18n, extensions, testing, and deployment to Managed Runtime
Automatic project type detection and B2C Commerce workflows for your AI assistant. See MCP Installation
@@ -90,12 +96,14 @@ Install plugins at your preferred scope:
```bash [User Scope (default)]
claude plugin install b2c-cli
claude plugin install b2c
+claude plugin install storefront-next
claude plugin install b2c-dx-mcp
```
```bash [Project Scope]
claude plugin install b2c-cli --scope project
claude plugin install b2c --scope project
+claude plugin install storefront-next --scope project
claude plugin install b2c-dx-mcp --scope project
```
@@ -107,6 +115,7 @@ Verify, update, or uninstall:
claude plugin list
claude plugin marketplace update
claude plugin update b2c-cli@b2c-developer-tooling
+claude plugin update storefront-next@b2c-developer-tooling
claude plugin uninstall b2c-cli@b2c-developer-tooling
claude plugin marketplace remove b2c-developer-tooling
```
@@ -145,6 +154,7 @@ List available skills:
```bash
b2c setup skills b2c --list
b2c setup skills b2c-cli --list
+b2c setup skills storefront-next --list
```
Install to specific IDEs:
@@ -240,3 +250,7 @@ Once installed, ask your AI assistant:
- "Help me create a Custom API for loyalty information"
- "Add logging to my checkout controller"
- "Create an HTTP service to call the payment gateway API"
+- "Set up a new Storefront Next project"
+- "Add a new route with a loader to my Storefront Next app"
+- "Deploy my Storefront Next storefront to Managed Runtime"
+- "Add Page Designer support to my storefront component"
diff --git a/packages/b2c-cli/src/commands/setup/skills.ts b/packages/b2c-cli/src/commands/setup/skills.ts
index 33823a72..9b808121 100644
--- a/packages/b2c-cli/src/commands/setup/skills.ts
+++ b/packages/b2c-cli/src/commands/setup/skills.ts
@@ -62,7 +62,7 @@ interface SetupSkillsResponse {
export default class SetupSkills extends BaseCommand {
static args = {
skillset: Args.string({
- description: 'Skill set to install: b2c, b2c-cli, or cap-dev',
+ description: 'Skill set to install: b2c, b2c-cli, storefront-next, or cap-dev',
options: ALL_SKILL_SETS,
}),
};
@@ -77,6 +77,7 @@ export default class SetupSkills extends BaseCommand {
static examples = [
'<%= config.bin %> <%= command.id %> b2c',
'<%= config.bin %> <%= command.id %> b2c-cli --ide cursor --global',
+ '<%= config.bin %> <%= command.id %> storefront-next --ide cursor',
'<%= config.bin %> <%= command.id %> cap-dev --ide claude-code --global',
'<%= config.bin %> <%= command.id %> b2c --list',
'<%= config.bin %> <%= command.id %> b2c-cli --skill b2c-code --skill b2c-webdav --ide cursor',
@@ -133,7 +134,7 @@ export default class SetupSkills extends BaseCommand {
this.error(
t(
'commands.setup.skills.skillsetRequired',
- 'Skillset argument required in non-interactive mode. Specify b2c, b2c-cli, or cap-dev.',
+ 'Skillset argument required in non-interactive mode. Specify b2c, b2c-cli, storefront-next, or cap-dev.',
),
);
} else {
@@ -269,7 +270,8 @@ export default class SetupSkills extends BaseCommand {
'Note: For Claude Code, we recommend using the plugin marketplace for automatic updates:\n' +
' claude plugin marketplace add SalesforceCommerceCloud/b2c-developer-tooling\n' +
' claude plugin install b2c-cli\n' +
- ' claude plugin install b2c\n\n' +
+ ' claude plugin install b2c\n' +
+ ' claude plugin install storefront-next\n\n' +
'Use --ide manual for manual installation to the same paths.',
),
);
diff --git a/packages/b2c-cli/src/i18n/locales/en.ts b/packages/b2c-cli/src/i18n/locales/en.ts
index 2ced47e6..1936e6fb 100644
--- a/packages/b2c-cli/src/i18n/locales/en.ts
+++ b/packages/b2c-cli/src/i18n/locales/en.ts
@@ -179,14 +179,16 @@ export const en = {
'Note: For Claude Code, we recommend using the plugin marketplace for automatic updates:\n' +
' claude plugin marketplace add SalesforceCommerceCloud/b2c-developer-tooling\n' +
' claude plugin install b2c-cli\n' +
- ' claude plugin install b2c\n\n' +
+ ' claude plugin install b2c\n' +
+ ' claude plugin install storefront-next\n\n' +
'Use --ide manual for manual installation to the same paths.',
preview: 'Installing {{count}} skills to {{ides}} ({{scope}})',
cancelled: 'Installation cancelled.',
installed: 'Successfully installed {{count}} skill(s):',
skippedCount: 'Skipped {{count}} skill(s):',
errorsCount: 'Failed to install {{count}} skill(s):',
- skillsetRequired: 'Skillset argument required in non-interactive mode. Specify b2c or b2c-cli.',
+ skillsetRequired:
+ 'Skillset argument required in non-interactive mode. Specify b2c, b2c-cli, storefront-next, or cap-dev.',
selectSkillset: 'Select skill set(s) to install:',
noSkillsetsSelected: 'No skill sets selected.',
selectIdes: 'Select target IDEs:',
diff --git a/packages/b2c-tooling-sdk/src/skills/github.ts b/packages/b2c-tooling-sdk/src/skills/github.ts
index 5f50ac3d..58121b58 100644
--- a/packages/b2c-tooling-sdk/src/skills/github.ts
+++ b/packages/b2c-tooling-sdk/src/skills/github.ts
@@ -135,9 +135,11 @@ function parseRelease(release: {
}): ReleaseInfo {
const b2cSource = getSkillSource('b2c');
const b2cCliSource = getSkillSource('b2c-cli');
+ const sfNextSource = getSkillSource('storefront-next');
const b2cAsset = release.assets.find((a) => a.name === b2cSource.assetName);
const b2cCliAsset = release.assets.find((a) => a.name === b2cCliSource.assetName);
+ const sfNextAsset = release.assets.find((a) => a.name === sfNextSource.assetName);
const versionMatch = release.tag_name.match(/@(\d+\.\d+\.\d+.*)$/);
const version = versionMatch ? versionMatch[1] : release.tag_name.replace(/^v/, '');
@@ -148,6 +150,7 @@ function parseRelease(release: {
publishedAt: release.published_at,
b2cSkillsAssetUrl: b2cAsset?.browser_download_url ?? null,
b2cCliSkillsAssetUrl: b2cCliAsset?.browser_download_url ?? null,
+ storefrontNextSkillsAssetUrl: sfNextAsset?.browser_download_url ?? null,
};
}
@@ -228,7 +231,7 @@ export async function listReleases(limit: number = 10): Promise {
return data
.filter((r) => r.tag_name.startsWith('b2c-agent-plugins@'))
.map(parseRelease)
- .filter((r) => r.b2cSkillsAssetUrl || r.b2cCliSkillsAssetUrl);
+ .filter((r) => r.b2cSkillsAssetUrl || r.b2cCliSkillsAssetUrl || r.storefrontNextSkillsAssetUrl);
}
/**
diff --git a/packages/b2c-tooling-sdk/src/skills/sources.ts b/packages/b2c-tooling-sdk/src/skills/sources.ts
index 50a5693b..34fc5adb 100644
--- a/packages/b2c-tooling-sdk/src/skills/sources.ts
+++ b/packages/b2c-tooling-sdk/src/skills/sources.ts
@@ -28,6 +28,14 @@ export const SKILL_SOURCES: Record = {
assetName: 'b2c-cli-skills.zip',
tagPattern: pluginsTag,
},
+ 'storefront-next': {
+ id: 'storefront-next',
+ displayName: 'Storefront Next development skills',
+ type: 'release-artifact',
+ repo: 'SalesforceCommerceCloud/b2c-developer-tooling',
+ assetName: 'storefront-next-skills.zip',
+ tagPattern: pluginsTag,
+ },
'cap-dev': {
id: 'cap-dev',
displayName: 'Commerce Apps development skills',
diff --git a/packages/b2c-tooling-sdk/src/skills/types.ts b/packages/b2c-tooling-sdk/src/skills/types.ts
index 700b930e..8029c392 100644
--- a/packages/b2c-tooling-sdk/src/skills/types.ts
+++ b/packages/b2c-tooling-sdk/src/skills/types.ts
@@ -20,7 +20,7 @@ export type IdeType =
/**
* Skill set categories matching the plugins directory structure.
*/
-export type SkillSet = 'b2c' | 'b2c-cli' | 'cap-dev';
+export type SkillSet = 'b2c' | 'b2c-cli' | 'cap-dev' | 'storefront-next';
/**
* Configuration for a skill source — defines how to fetch skills from a particular repository.
@@ -92,6 +92,8 @@ export interface ReleaseInfo {
b2cSkillsAssetUrl: string | null;
/** Download URL for b2c-cli-skills.zip asset, or null if not present */
b2cCliSkillsAssetUrl: string | null;
+ /** Download URL for storefront-next-skills.zip asset, or null if not present */
+ storefrontNextSkillsAssetUrl: string | null;
}
/**
diff --git a/packages/b2c-tooling-sdk/test/skills/sources.test.ts b/packages/b2c-tooling-sdk/test/skills/sources.test.ts
index 3b5f9324..d9e9c733 100644
--- a/packages/b2c-tooling-sdk/test/skills/sources.test.ts
+++ b/packages/b2c-tooling-sdk/test/skills/sources.test.ts
@@ -49,7 +49,8 @@ describe('skill sources', () => {
expect(ALL_SKILL_SETS).to.include('b2c');
expect(ALL_SKILL_SETS).to.include('b2c-cli');
expect(ALL_SKILL_SETS).to.include('cap-dev');
- expect(ALL_SKILL_SETS).to.have.lengthOf(3);
+ expect(ALL_SKILL_SETS).to.include('storefront-next');
+ expect(ALL_SKILL_SETS).to.have.lengthOf(4);
});
});
diff --git a/scripts/sync-plugin-versions.mjs b/scripts/sync-plugin-versions.mjs
index 2bc67385..5360211b 100644
--- a/scripts/sync-plugin-versions.mjs
+++ b/scripts/sync-plugin-versions.mjs
@@ -34,7 +34,7 @@ if (!version) {
// b2c-dx-mcp is NOT part of b2c-agent-plugins — it tracks @salesforce/b2c-dx-mcp separately.
const marketplacePath = join(repoRoot, '.claude-plugin/marketplace.json');
const marketplace = readJson(marketplacePath);
-const claudeTargets = new Set(['b2c-cli', 'b2c']);
+const claudeTargets = new Set(['b2c-cli', 'b2c', 'storefront-next']);
for (const plugin of marketplace.plugins) {
if (claudeTargets.has(plugin.name)) {
plugin.version = version;
@@ -46,6 +46,7 @@ writeJson(marketplacePath, marketplace);
const codexTargets = [
'skills/b2c-cli/.codex-plugin/plugin.json',
'skills/b2c/.codex-plugin/plugin.json',
+ 'skills/storefront-next/.codex-plugin/plugin.json',
];
for (const rel of codexTargets) {
const path = join(repoRoot, rel);
diff --git a/skills/storefront-next/.codex-plugin/plugin.json b/skills/storefront-next/.codex-plugin/plugin.json
new file mode 100644
index 00000000..af2a33b8
--- /dev/null
+++ b/skills/storefront-next/.codex-plugin/plugin.json
@@ -0,0 +1,39 @@
+{
+ "name": "storefront-next",
+ "version": "1.1.2",
+ "description": "Storefront Next development skills for building React 19 storefronts on Salesforce B2C Commerce.",
+ "author": {
+ "name": "Salesforce"
+ },
+ "homepage": "https://salesforcecommercecloud.github.io/b2c-developer-tooling/",
+ "repository": "https://github.com/SalesforceCommerceCloud/b2c-developer-tooling",
+ "license": "Apache-2.0",
+ "keywords": [
+ "salesforce",
+ "storefront-next",
+ "react",
+ "b2c-commerce",
+ "commerce-cloud"
+ ],
+ "skills": "./skills/",
+ "interface": {
+ "displayName": "Storefront Next",
+ "shortDescription": "Build Salesforce B2C Storefront Next projects with AI assistance.",
+ "longDescription": "Skills for developing React 19 storefronts on Salesforce B2C Commerce — routing, data fetching, components, Page Designer, authentication, i18n, extensions, testing, and deployment to Managed Runtime.",
+ "developerName": "Salesforce",
+ "category": "Productivity",
+ "capabilities": [
+ "Read"
+ ],
+ "logo": "./assets/logo.svg",
+ "composerIcon": "./assets/logo.svg",
+ "brandColor": "#0D9DDA",
+ "websiteURL": "https://salesforcecommercecloud.github.io/b2c-developer-tooling/",
+ "defaultPrompt": [
+ "Set up a new Storefront Next project",
+ "Add a new route with data fetching to my storefront",
+ "Deploy my storefront to Managed Runtime",
+ "Add Page Designer support to my storefront component"
+ ]
+ }
+}
diff --git a/skills/storefront-next/README.md b/skills/storefront-next/README.md
new file mode 100644
index 00000000..b07d968d
--- /dev/null
+++ b/skills/storefront-next/README.md
@@ -0,0 +1,48 @@
+# storefront-next
+
+Agent skills for building Salesforce B2C Storefront Next projects — React 19 storefronts with routing, data fetching, Page Designer, authentication, i18n, extensions, and deployment to Managed Runtime.
+
+Part of the [B2C Developer Tooling](https://github.com/SalesforceCommerceCloud/b2c-developer-tooling) marketplace.
+
+## Installation
+
+```bash
+# Claude Code
+claude plugin marketplace add SalesforceCommerceCloud/b2c-developer-tooling
+claude plugin install storefront-next@b2c-developer-tooling
+
+# GitHub Copilot CLI
+copilot plugin marketplace add SalesforceCommerceCloud/b2c-developer-tooling
+copilot plugin install storefront-next@b2c-developer-tooling
+```
+
+**VS Code (GitHub Copilot):** Command Palette → **Chat: Install Plugin From Source** → enter the repo `SalesforceCommerceCloud/b2c-developer-tooling`.
+
+**Codex:** open the repo as a workspace, restart Codex, then install from the **B2C Developer Tooling** marketplace in the plugin directory.
+
+For file-copy install to any supported IDE, use `b2c setup skills storefront-next`. See the [install guide](https://salesforcecommercecloud.github.io/b2c-developer-tooling/guide/agent-skills) for details.
+
+## What's included
+
+Skills covering the full Storefront Next development lifecycle:
+
+- **`sfnext-project-setup`** — project creation, environment configuration, project structure
+- **`sfnext-routing`** — React Router 7 file-based routing with flat-routes conventions
+- **`sfnext-data-fetching`** — server-side loaders, actions, useScapiFetcher
+- **`sfnext-components`** — UI components, createPage HOC, shadcn/ui, Tailwind CSS v4
+- **`sfnext-configuration`** — config.server.ts, environment variables, multi-site setup
+- **`sfnext-page-designer`** — Page Designer integration with React decorators and component registry
+- **`sfnext-extensions`** — extension system, target points, extension routes
+- **`sfnext-authentication`** — split-cookie SLAS architecture, auth middleware, session management
+- **`sfnext-i18n`** — internationalization with i18next, dual-instance server/client, namespaces
+- **`sfnext-state-management`** — React context, Zustand stores, basket provider
+- **`sfnext-testing`** — Vitest unit tests, Storybook stories, interaction and accessibility testing
+- **`sfnext-performance`** — bundle size limits, DynamicImage, parallel fetching, Lighthouse optimization
+- **`sfnext-deployment`** — build and deploy to Managed Runtime (MRT), cartridge deployment
+- **`sfnext-hybrid-storefronts`** — hybrid setup with SFRA/SiteGenesis, gradual migration, session bridging
+
+See [`skills/`](./skills/) for the full list.
+
+## License
+
+Apache-2.0. See the [repo LICENSE](https://github.com/SalesforceCommerceCloud/b2c-developer-tooling/blob/main/LICENSE.txt).
diff --git a/skills/storefront-next/assets/logo.svg b/skills/storefront-next/assets/logo.svg
new file mode 100644
index 00000000..b2f3856b
--- /dev/null
+++ b/skills/storefront-next/assets/logo.svg
@@ -0,0 +1,31 @@
+
+
\ No newline at end of file
diff --git a/skills/storefront-next/skills/sfnext-authentication/SKILL.md b/skills/storefront-next/skills/sfnext-authentication/SKILL.md
new file mode 100644
index 00000000..f6545d4a
--- /dev/null
+++ b/skills/storefront-next/skills/sfnext-authentication/SKILL.md
@@ -0,0 +1,120 @@
+---
+name: sfnext-authentication
+description: Implement authentication in Storefront Next using split-cookie architecture, SLAS tokens, and auth middleware. Use when accessing user identity in loaders, detecting guest vs registered users, using getAuth or useAuth, or understanding session management and token refresh.
+---
+
+# Authentication Skill
+
+This skill covers Storefront Next's split-cookie authentication architecture with SLAS (Shopper Login and API Access Service).
+
+## Overview
+
+Storefront Next uses a split-cookie architecture separating server and client auth concerns:
+
+- **Server middleware** (`auth.server.ts`) — Manages SLAS tokens, writes cookies, handles token refresh
+- **React Context** (`AuthProvider`) — Provides public session data (non-sensitive fields) to components via `useAuth()`
+
+## Cookie Design
+
+| Cookie | Purpose | User Type | Expiry | HttpOnly |
+|--------|---------|-----------|--------|----------|
+| `cc-nx-g` | Guest refresh token | Guest | 30 days | No |
+| `cc-nx` | Registered refresh token | Registered | 90 days | No |
+| `cc-at` | Access token | Both | 30 min | No |
+| `usid` | User session ID | Both | Matches refresh | No |
+| `customerId` | Customer ID | Registered | Matches refresh | No |
+
+**Key points:**
+- Only ONE refresh token exists at a time (guest OR registered, never both)
+- User type is derived from which refresh token is present
+- Cookies are auto-namespaced with `siteId`
+- Tokens auto-refresh when expired
+
+## Usage in Loaders/Actions
+
+```typescript
+import { getAuth } from '@/middlewares/auth.server';
+
+export function loader({ context }: LoaderFunctionArgs) {
+ const auth = getAuth(context);
+
+ const { accessToken, customerId, userType } = auth;
+ const isGuest = userType === 'guest';
+ const isRegistered = userType === 'registered';
+
+ return { isGuest, customerId };
+}
+```
+
+## Usage in Components
+
+```typescript
+import { useAuth } from '@/providers/auth';
+
+export function MyComponent() {
+ const auth = useAuth();
+
+ if (auth?.userType === 'guest') {
+ return ;
+ }
+
+ return
Welcome, customer {auth?.customerId}
;
+}
+```
+
+## Common Patterns
+
+### Protected Routes
+
+```typescript
+export function loader({ context }: LoaderFunctionArgs) {
+ const auth = getAuth(context);
+
+ if (auth.userType === 'guest') {
+ throw redirect('/login');
+ }
+
+ const clients = createApiClients(context);
+ return {
+ orders: clients.shopperOrders.getOrders({
+ params: { path: { customerId: auth.customerId } }
+ }).then(({ data }) => data),
+ };
+}
+```
+
+### Conditional Data Loading
+
+```typescript
+export function loader({ context }: LoaderFunctionArgs) {
+ const auth = getAuth(context);
+ const clients = createApiClients(context);
+
+ const base = {
+ products: clients.shopperProducts.getProducts({...}).then(({ data }) => data),
+ };
+
+ if (auth.userType === 'registered') {
+ return {
+ ...base,
+ wishlist: clients.shopperCustomers.getWishlist({...}).then(({ data }) => data),
+ };
+ }
+
+ return base;
+}
+```
+
+## Troubleshooting
+
+| Issue | Cause | Solution |
+|-------|-------|----------|
+| `auth` is undefined | Missing auth middleware | Ensure `auth.server.ts` middleware is configured |
+| Always guest | Refresh token expired | Check cookie expiry; SLAS auto-refreshes |
+| Token errors in SCAPI | Stale access token | Tokens auto-refresh; check SLAS client configuration |
+
+## Related Skills
+
+- `storefront-next:sfnext-data-fetching` - Using auth context in loader functions
+- `storefront-next:sfnext-configuration` - SLAS client configuration
+- `storefront-next:sfnext-hybrid-storefronts` - Session bridging with SFRA
diff --git a/skills/storefront-next/skills/sfnext-components/SKILL.md b/skills/storefront-next/skills/sfnext-components/SKILL.md
new file mode 100644
index 00000000..c33d6879
--- /dev/null
+++ b/skills/storefront-next/skills/sfnext-components/SKILL.md
@@ -0,0 +1,191 @@
+---
+name: sfnext-components
+description: Build UI components in Storefront Next using createPage HOC, Suspense/Await patterns, shadcn/ui, and Tailwind CSS v4. Use when creating page components, adding Suspense boundaries, integrating shadcn/ui, styling with Tailwind, or organizing component files. Covers server vs client rendering patterns and the cn() utility.
+---
+
+# Components Skill
+
+This skill covers component development patterns in Storefront Next — createPage HOC, Suspense boundaries, shadcn/ui integration, and Tailwind CSS styling.
+
+## Page Component Pattern
+
+Most routes export a default function component that receives `loaderData` as props:
+
+```typescript
+import { Suspense } from 'react';
+import { Await } from 'react-router';
+import { SeoMeta } from '@/components/seo-meta';
+
+type ProductPageData = {
+ product: Promise;
+ reviews: Promise;
+};
+
+export default function ProductPage({ loaderData }: { loaderData: ProductPageData }) {
+ return (
+ <>
+
+ }>
+
+ {(product) => }
+
+
+ }>
+
+ {(reviews) => }
+
+
+ >
+ );
+}
+```
+
+### createPage HOC (Optional)
+
+For pages needing standardized Suspense wrappers and page key management:
+
+```typescript
+import { createPage } from '@/components/create-page';
+
+export default createPage({
+ component: ProductView,
+ fallback: ,
+});
+```
+
+## Suspense Boundaries and Code Splitting
+
+Use `` + `` for streaming loader data, and `lazy()` for code-splitting heavy components:
+
+```typescript
+import { lazy, Suspense } from 'react';
+
+// Code-split a heavy component
+const CustomerReviewsSection = lazy(() =>
+ import('@/components/customer-reviews-section/customer-reviews-section')
+);
+
+export default function ProductPage({ loaderData }: { loaderData: ProductPageData }) {
+ return (
+ <>
+ {/* Stream loader data */}
+ }>
+
+ {(product) => }
+
+
+
+ {/* Code-split component */}
+ }>
+
+
+ >
+ );
+}
+```
+
+## shadcn/ui Components
+
+shadcn/ui provides pre-built accessible components. Add them via CLI:
+
+```bash
+npx shadcn@latest add button
+npx shadcn@latest add card
+npx shadcn@latest add dialog
+```
+
+**Rules:**
+
+- Add components via CLI only (do not manually create files in `src/components/ui/`)
+- `src/components/ui/` components are copied into your project and can be customized directly
+- Keep app/domain components outside `src/components/ui/`:
+
+```typescript
+// Optional wrapper for app-specific styling
+import { Button } from '@/components/ui/button';
+
+export function PrimaryButton(props: React.ComponentProps) {
+ return ;
+}
+```
+
+## Tailwind CSS Styling
+
+Tailwind CSS v4 is the only permitted styling approach:
+
+```typescript
+import { cn } from '@/lib/utils';
+
+export function ProductCard({ featured }: { featured?: boolean }) {
+ return (
+
+
+ Product Name
+
+
+ );
+}
+```
+
+**Rules:**
+
+- Use Tailwind utility classes as the primary styling approach
+- Use `cn()` for conditional class merging
+- Follow mobile-first responsive design (`md:`, `lg:` breakpoints)
+- Prefer Tailwind over inline styles; inline styles are acceptable for truly dynamic values (e.g., `backgroundColor` from API data)
+- No CSS modules or separate CSS files
+- Custom global CSS only in `src/app.css`
+
+### Dark Mode
+
+Theme variables automatically adapt via CSS variables:
+
+```typescript
+
+
+
+```
+
+### Responsive Design
+
+```typescript
+
+ {products.map(p => )}
+
+```
+
+## File Organization
+
+```
+src/components/product-tile/
+├── index.tsx # Component implementation
+├── index.test.tsx # Vitest unit tests
+└── stories/
+ ├── index.stories.tsx # Storybook stories
+ └── __snapshots__/ # Storybook snapshots (optional)
+
+src/components/product-skeleton/
+├── index.tsx # Skeleton component (separate from main)
+├── index.test.tsx
+└── stories/
+ └── index.stories.tsx
+```
+
+## Best Practices
+
+1. **Export default function components** — Receive `loaderData` as props
+2. **Granular Suspense boundaries** — Show content progressively as data resolves
+3. **Use `lazy()` for heavy components** — Code-split below-the-fold or conditional UI
+4. **Reusable skeleton components** — Consistent loading states
+5. **Colocate tests and stories** — Keep test files next to source files
+6. **TypeScript interfaces** — Define proper types for all props
+
+## Related Skills
+
+- `storefront-next:sfnext-data-fetching` - Loader patterns that feed data to components
+- `storefront-next:sfnext-testing` - Writing Vitest tests and Storybook stories
+- `storefront-next:sfnext-page-designer` - Page Designer component integration
+- `storefront-next:sfnext-i18n` - Translating component text
diff --git a/skills/storefront-next/skills/sfnext-configuration/SKILL.md b/skills/storefront-next/skills/sfnext-configuration/SKILL.md
new file mode 100644
index 00000000..81f4bd4c
--- /dev/null
+++ b/skills/storefront-next/skills/sfnext-configuration/SKILL.md
@@ -0,0 +1,160 @@
+---
+name: sfnext-configuration
+description: Manage Storefront Next application configuration using config.server.ts, schema types, environment variables, and multi-site setup. Use when editing config.server.ts, adding PUBLIC__ environment variables, using useConfig or getConfig, or configuring multiple sites. NOT for initial project creation — see sfnext-project-setup.
+---
+
+# Configuration Skill
+
+This skill covers the Storefront Next configuration system — centralized in `config.server.ts` with environment variable overrides.
+
+## Overview
+
+All configuration is centralized in `config.server.ts` with typed defaults. Environment variables (via `.env` files or MRT settings) override these defaults. The system provides type-safe access with automatic parsing and validation.
+
+## Adding Configuration
+
+### 1. Define the type in `src/types/config.ts`
+
+```typescript
+export type Config = {
+ app: {
+ myFeature: {
+ enabled: boolean;
+ maxItems: number;
+ };
+ };
+};
+```
+
+### 2. Set defaults in `config.server.ts`
+
+```typescript
+export default defineConfig({
+ app: {
+ myFeature: {
+ enabled: false,
+ maxItems: 10,
+ },
+ },
+});
+```
+
+### 3. Override via environment variables
+
+```bash
+PUBLIC__app__myFeature__enabled=true
+PUBLIC__app__myFeature__maxItems=20
+```
+
+## Accessing Configuration
+
+### In React Components
+
+```typescript
+import { useConfig } from '@salesforce/storefront-next-runtime/config';
+
+export function MyComponent() {
+ const config = useConfig();
+
+ if (config.myFeature.enabled) {
+ const maxItems = config.myFeature.maxItems;
+ // Feature code
+ }
+}
+```
+
+### In Server Loaders/Actions
+
+```typescript
+import { getConfig } from '@salesforce/storefront-next-runtime/config';
+
+export function loader({context}: LoaderFunctionArgs) {
+ const config = getConfig(context); // context is required on server
+
+ if (config.myFeature.enabled) {
+ // Loader code
+ }
+}
+```
+
+### In Browser Code (Non-Route Modules)
+
+```typescript
+import { getConfig } from '@salesforce/storefront-next-runtime/config';
+
+export function getFeatureFlag() {
+ const config = getConfig(); // No context needed in browser (uses window.__APP_CONFIG__)
+ return config.myFeature.enabled;
+}
+```
+
+**Note:** `getConfig()` and `useConfig()` return `AppConfig` — the `app` section of the full config. Access properties directly (e.g., `config.myFeature.enabled`) without the `app` prefix.
+
+## Environment Variable Rules
+
+```bash
+# Pattern: PUBLIC__app__{path}__{to}__{property}=value
+PUBLIC__app__commerce__api__clientId=abc123
+# Maps to config.app.commerce.api.clientId
+# Accessed as config.commerce.api.clientId
+```
+
+| Rule | Detail |
+| ----------------- | ---------------------------------------------- |
+| `PUBLIC__` prefix | Exposed to browser (client-safe) |
+| No prefix | Server-only (secrets) |
+| `__` separator | Navigate nested paths |
+| Auto-parsing | Numbers, booleans, JSON parsed automatically |
+| Validation | Paths must exist in `config.server.ts` |
+| Depth limit | Maximum 10 levels |
+| MRT limits | Names max 512 chars; total `PUBLIC__` max 32KB |
+
+## Multi-Site Configuration
+
+```bash
+PUBLIC__app__commerce__sites='[
+ {
+ "id": "RefArchGlobal",
+ "defaultLocale": "en-US",
+ "defaultCurrency": "USD",
+ "supportedLocales": [
+ {"id": "en-US", "preferredCurrency": "USD"},
+ {"id": "de-DE", "preferredCurrency": "EUR"}
+ ],
+ "supportedCurrencies": ["USD", "EUR"]
+ }
+]'
+```
+
+```typescript
+const config = getConfig(context);
+const currentSite = config.commerce.sites[0];
+const locale = currentSite.defaultLocale; // "en-US"
+const currency = currentSite.defaultCurrency; // "USD"
+```
+
+## Security
+
+```bash
+# Client-safe (PUBLIC__ prefix)
+PUBLIC__app__commerce__api__clientId=abc123
+
+# Server-only (no prefix — never sent to client)
+COMMERCE_API_SLAS_SECRET=your-secret
+```
+
+Read server-only secrets directly from `process.env` — never add them to the config system.
+
+## Common Pitfalls
+
+| Pitfall | Problem | Solution |
+| ----------------- | ------------------------------------------ | -------------------------------------------------- |
+| Missing `context` | `getConfig()` returns undefined in loaders | Use `getConfig(context)` on server |
+| Typo in env var | Variable silently ignored | Validation catches paths not in `config.server.ts` |
+| Exposing secrets | Sensitive data in browser | Use no-prefix variables; access via `process.env` |
+
+## Related Skills
+
+- `storefront-next:sfnext-project-setup` - Initial environment setup and `.env` configuration
+- `storefront-next:sfnext-data-fetching` - Using config in loader functions
+- `storefront-next:sfnext-deployment` - MRT environment variable configuration
diff --git a/skills/storefront-next/skills/sfnext-data-fetching/SKILL.md b/skills/storefront-next/skills/sfnext-data-fetching/SKILL.md
new file mode 100644
index 00000000..326cc2d9
--- /dev/null
+++ b/skills/storefront-next/skills/sfnext-data-fetching/SKILL.md
@@ -0,0 +1,188 @@
+---
+name: sfnext-data-fetching
+description: Implement server-side data fetching in Storefront Next using loaders, actions, and useScapiFetcher. Use when writing loader functions, making SCAPI calls, handling form submissions, or implementing interactive data fetching. Covers synchronous loaders, streaming patterns, createApiClients, and parallel requests. NOT for client-side Zustand state — see sfnext-state-management.
+---
+
+# Data Fetching Skill
+
+This skill covers server-side data fetching patterns in Storefront Next — loaders, actions, and the useScapiFetcher hook.
+
+## Overview
+
+Storefront Next mandates **server-only data loading**. All SCAPI requests execute on the MRT server, never in the browser. Three mechanisms exist:
+
+| Mechanism | When It Runs | Use Case |
+|-----------|-------------|----------|
+| `loader` | Route navigation | Initial page data |
+| `action` | Form submission | Mutations (add to cart, update profile) |
+| `useScapiFetcher` | User interaction | On-demand fetching (search suggestions, infinite scroll) |
+
+## Loader Patterns
+
+Loaders can be **synchronous** (returning promises for streaming) or **async** (awaiting critical data). Choose based on what the page needs:
+
+- **Sync loader** — Returns promises directly. Enables streaming SSR: the shell renders immediately while data streams in. Best when all data can render progressively.
+- **Async loader** — Awaits critical data before rendering. Use when data is required for SEO or the page shell (e.g., category name in breadcrumbs). Non-critical data can still be returned as promises for streaming.
+
+```typescript
+// Sync — full streaming (all data renders progressively)
+export function loader({ params, context }: LoaderFunctionArgs): ProductPageData {
+ const clients = createApiClients(context);
+ return {
+ product: clients.shopperProducts.getProduct({
+ params: { path: { id: params.productId } }
+ }).then(({ data }) => data),
+ reviews: clients.shopperProducts.getReviews({
+ params: { path: { id: params.productId } }
+ }).then(({ data }) => data),
+ };
+}
+
+// Async — await critical data, stream the rest (mixed strategy)
+export async function loader({ params, context }: LoaderFunctionArgs): Promise {
+ const clients = createApiClients(context);
+
+ // Await critical data needed for page shell/SEO
+ const category = await clients.shopperProducts.getCategory({
+ params: { path: { id: params.categoryId } }
+ }).then(({ data }) => data);
+
+ return {
+ category, // Resolved immediately
+ products: clients.shopperSearch.productSearch({
+ params: { query: { q: '', refine: { cgid: params.categoryId } } }
+ }).then(({ data }) => data), // Streamed
+ };
+}
+```
+
+## When to Use Each Pattern
+
+| Pattern | When | Example |
+|---------|------|---------|
+| Sync (full streaming) | All data can render progressively | Product page with reviews |
+| Async (await critical) | SEO-critical data needed for page shell | Category page (needs category name) |
+| Mixed | Some data critical, some deferrable | Category name (await) + product grid (stream) |
+
+See [Loader Patterns Reference](references/LOADER-PATTERNS.md) for more patterns and data flow diagrams.
+
+## Action Functions
+
+Handle mutations (form submissions, cart updates):
+
+```typescript
+import { data, redirect } from 'react-router';
+
+export async function action({ request, context }: ActionFunctionArgs) {
+ const formData = await request.formData();
+ const productId = formData.get('productId') as string;
+
+ const clients = createApiClients(context);
+
+ try {
+ await clients.shopperBasketsV2.addItemToBasket({
+ params: {
+ path: { basketId },
+ body: { productId, quantity: 1 },
+ },
+ });
+ return data({ success: true });
+ } catch (error) {
+ return data({ success: false, error: error.message }, { status: 400 });
+ }
+}
+```
+
+## useScapiFetcher — Interactive Data Fetching
+
+For on-demand data fetching triggered by user interactions (after page load):
+
+```typescript
+import { useScapiFetcher } from '@/hooks/use-scapi-fetcher';
+
+export function useSearchSuggestions({ q, limit, currency }) {
+ const parameters = useMemo(
+ () => ({ params: { query: { q, limit, currency } } }),
+ [q, limit, currency]
+ );
+
+ const fetcher = useScapiFetcher(
+ 'shopperSearch',
+ 'getSearchSuggestions',
+ parameters
+ );
+
+ const refetch = useCallback(async () => {
+ await fetcher.load();
+ }, [fetcher]);
+
+ return {
+ data: fetcher.data,
+ isLoading: fetcher.state === 'loading',
+ refetch,
+ };
+}
+```
+
+See [SCAPI Fetcher Reference](references/SCAPI-FETCHER.md) for the complete useScapiFetcher API.
+
+## API Client Usage
+
+Always use `createApiClients(context)` in loaders and actions:
+
+```typescript
+import { createApiClients } from '@/lib/api-clients';
+
+export function loader({ context }: LoaderFunctionArgs) {
+ const clients = createApiClients(context);
+
+ clients.shopperProducts.getProduct({...});
+ clients.shopperCustomers.getCustomer({...});
+ clients.shopperBasketsV2.getBasket({...});
+ clients.shopperSearch.productSearch({...});
+ clients.shopperOrders.getOrder({...});
+}
+```
+
+## Parallel vs Sequential Requests
+
+```typescript
+// GOOD — Parallel requests (all start simultaneously)
+export function loader({ context }: LoaderFunctionArgs) {
+ const clients = createApiClients(context);
+ return {
+ product: clients.shopperProducts.getProduct({...}).then(({ data }) => data),
+ reviews: clients.shopperProducts.getReviews({...}).then(({ data }) => data),
+ recommendations: clients.shopperProducts.getRecommendations({...}).then(({ data }) => data),
+ };
+}
+
+// AVOID — Sequential awaits of independent requests (unnecessarily slow)
+export async function loader({ context }: LoaderFunctionArgs) {
+ const clients = createApiClients(context);
+ const product = await clients.shopperProducts.getProduct({...}); // Waits...
+ const reviews = await clients.shopperProducts.getReviews({...}); // Then waits again
+ return { product, reviews };
+}
+```
+
+## Common Pitfalls
+
+| Pitfall | Problem | Solution |
+|---------|---------|----------|
+| Awaiting all data | Blocks page transition unnecessarily | Only await SEO-critical data; stream the rest |
+| Client loaders | Not permitted in Storefront Next | Use server `loader` or `useScapiFetcher` |
+| Sequential `await` | Slow data loading | Return promises in parallel |
+| Missing `context` in `getConfig()` | Config unavailable | Pass `context` in server loaders: `getConfig(context)` |
+
+## Related Skills
+
+- `storefront-next:sfnext-routing` - Route file conventions and module exports
+- `storefront-next:sfnext-components` - Rendering loader data with createPage and Suspense
+- `storefront-next:sfnext-state-management` - Client-side Zustand stores (NOT data fetching)
+- `storefront-next:sfnext-authentication` - Auth context in loaders
+
+## Reference Documentation
+
+- [Loader Patterns Reference](references/LOADER-PATTERNS.md) - Data flow diagrams and advanced patterns
+- [SCAPI Fetcher Reference](references/SCAPI-FETCHER.md) - Complete useScapiFetcher API and examples
diff --git a/skills/storefront-next/skills/sfnext-data-fetching/references/LOADER-PATTERNS.md b/skills/storefront-next/skills/sfnext-data-fetching/references/LOADER-PATTERNS.md
new file mode 100644
index 00000000..f77a2909
--- /dev/null
+++ b/skills/storefront-next/skills/sfnext-data-fetching/references/LOADER-PATTERNS.md
@@ -0,0 +1,102 @@
+# Loader Patterns Reference
+
+## Data Flow
+
+### Initial Page Load (SSR)
+
+```
+Browser request → MRT Server
+ ↓
+ loader() runs on server
+ ↓
+ SCAPI requests on MRT
+ ↓
+ HTML response streamed → Browser
+```
+
+### Subsequent Navigation (SPA)
+
+```
+User clicks link → React Router intercepts
+ ↓
+ Browser fetches from server
+ ↓
+ MRT Server runs same loader()
+ ↓
+ SCAPI requests on MRT
+ ↓
+ JSON response → Browser
+ ↓
+ React updates DOM
+```
+
+The loader code is identical for SSR and SPA navigation — only the response format differs (HTML vs JSON).
+
+## Decision Tree: Which Pattern to Use
+
+```
+Need data for page render?
+├── Critical for SEO / above-the-fold?
+│ └── Use awaited data (async loader with await)
+├── Non-critical / below-the-fold?
+│ └── Use deferred data (sync loader, return promises)
+└── Mix of both?
+ └── Use mixed strategy (await critical, stream rest)
+
+Need to handle mutations?
+└── Use action function
+
+Need data after page load (user interaction)?
+└── Use useScapiFetcher
+```
+
+## Pattern: Multiple API Calls with Dependencies
+
+When one API call depends on another's result:
+
+```typescript
+export async function loader({ params, context }: LoaderFunctionArgs) {
+ const clients = createApiClients(context);
+
+ // First call — must resolve before dependent calls
+ const category = await clients.shopperProducts.getCategory({
+ params: { path: { id: params.categoryId } }
+ }).then(({ data }) => data);
+
+ // Dependent calls — can run in parallel with each other
+ return {
+ category,
+ products: clients.shopperSearch.productSearch({
+ params: { query: { refine: `cgid=${category.id}` } }
+ }).then(({ data }) => data),
+ refinements: clients.shopperSearch.getSearchSuggestions({
+ params: { query: { q: category.name } }
+ }).then(({ data }) => data),
+ };
+}
+```
+
+## Pattern: Conditional Data Loading
+
+```typescript
+export function loader({ params, context }: LoaderFunctionArgs) {
+ const clients = createApiClients(context);
+ const auth = getAuth(context);
+
+ const base = {
+ product: clients.shopperProducts.getProduct({
+ params: { path: { id: params.productId } }
+ }).then(({ data }) => data),
+ };
+
+ // Only fetch wishlist for registered users
+ if (auth.userType === 'registered') {
+ return {
+ ...base,
+ wishlist: clients.shopperCustomers.getWishlist({...}).then(({ data }) => data),
+ };
+ }
+
+ return base;
+}
+```
diff --git a/skills/storefront-next/skills/sfnext-data-fetching/references/SCAPI-FETCHER.md b/skills/storefront-next/skills/sfnext-data-fetching/references/SCAPI-FETCHER.md
new file mode 100644
index 00000000..42ba878f
--- /dev/null
+++ b/skills/storefront-next/skills/sfnext-data-fetching/references/SCAPI-FETCHER.md
@@ -0,0 +1,93 @@
+# SCAPI Fetcher Reference
+
+## How useScapiFetcher Works
+
+```
+Component calls useScapiFetcher()
+ ↓
+Hook builds URL: /resource/api/client/{encoded-params}
+ ↓
+fetcher.load() or fetcher.submit()
+ ↓
+resource.api.client.$resource.ts loader/action runs ON SERVER
+ ↓
+createApiClients(context) makes SCAPI call (server-side)
+ ↓
+JSON response returned to component
+```
+
+Even though `useScapiFetcher` is called from the browser, the actual SCAPI requests happen **on the server** through the resource route, keeping credentials secure.
+
+## API
+
+```typescript
+const fetcher = useScapiFetcher(
+ clientName, // SCAPI client: 'shopperSearch', 'shopperProducts', etc.
+ methodName, // Method: 'getSearchSuggestions', 'productSearch', etc.
+ parameters // SCAPI parameters object
+);
+
+// Properties
+fetcher.data // Response data (undefined until loaded)
+fetcher.state // 'idle' | 'loading' | 'submitting'
+fetcher.load() // Trigger a GET request
+fetcher.submit() // Trigger a POST request
+```
+
+## loader vs useScapiFetcher
+
+| Scenario | Use |
+|----------|-----|
+| Load product data on page visit | `loader` |
+| Load checkout data | `loader` |
+| Search suggestions as user types | `useScapiFetcher` |
+| Update customer profile in modal | `useScapiFetcher` |
+| Load recommendations after page loads | `useScapiFetcher` |
+| Fetch bonus products when modal opens | `useScapiFetcher` |
+| Infinite scroll / Load more | `useScapiFetcher` |
+
+## Timeline Comparison
+
+```
+loader (Server):
+ [navigate] → [server fetch] → [stream to client]
+ Data available: Streamed during render via Suspense
+
+useScapiFetcher:
+ [render] → [user action] → [fetch] → [re-render]
+ Data available: AFTER user action, component re-renders
+```
+
+## Complete Example: Search Suggestions
+
+```typescript
+import { useScapiFetcher } from '@/hooks/use-scapi-fetcher';
+import { useMemo, useCallback } from 'react';
+
+export function useSearchSuggestions({ q, limit, currency }) {
+ const parameters = useMemo(
+ () => ({
+ params: {
+ query: { q, limit, currency }
+ }
+ }),
+ [q, limit, currency]
+ );
+
+ const fetcher = useScapiFetcher(
+ 'shopperSearch',
+ 'getSearchSuggestions',
+ parameters
+ );
+
+ const refetch = useCallback(async () => {
+ await fetcher.load();
+ }, [fetcher]);
+
+ return {
+ data: fetcher.data,
+ isLoading: fetcher.state === 'loading',
+ refetch
+ };
+}
+```
diff --git a/skills/storefront-next/skills/sfnext-deployment/SKILL.md b/skills/storefront-next/skills/sfnext-deployment/SKILL.md
new file mode 100644
index 00000000..169bf378
--- /dev/null
+++ b/skills/storefront-next/skills/sfnext-deployment/SKILL.md
@@ -0,0 +1,126 @@
+---
+name: sfnext-deployment
+description: Build and deploy Storefront Next storefronts to Managed Runtime (MRT) using the sfnext CLI. Use when running production builds, pushing bundles to MRT with sfnext push, configuring deployment environments, or deploying Page Designer cartridges. This is for Storefront Next deployment — for general MRT management via b2c CLI, see b2c-cli:b2c-mrt.
+---
+
+# Deployment Skill
+
+This skill covers building and deploying Storefront Next storefronts to Managed Runtime (MRT).
+
+## Overview
+
+Storefront Next storefronts are deployed to MRT as bundles. The `sfnext` CLI handles building and pushing bundles, while environment configuration is managed through MRT environment variables.
+
+## Production Build
+
+```bash
+# Build for production
+pnpm build
+
+# The build output goes to build/ directory
+```
+
+The production build:
+
+- Compiles TypeScript to JavaScript
+- Bundles client and server code separately
+- Optimizes and minifies assets
+- Generates the static Page Designer registry
+
+## Deploying to MRT
+
+### Using sfnext CLI
+
+```bash
+# Push the current build to MRT
+pnpm push
+
+# Push with a specific message
+pnpm sfnext push -m "Release v1.2.0"
+
+# Push to a specific environment
+pnpm sfnext push --environment staging --wait
+```
+
+### Deployment Flow
+
+```
+pnpm build → pnpm push → MRT receives bundle → Deployed to environment
+```
+
+See [MRT Deployment Reference](references/MRT-DEPLOYMENT.md) for detailed deployment options.
+
+## Environment Configuration
+
+Environment variables for MRT are configured through:
+
+1. **MRT Dashboard** — Set `PUBLIC__` variables per environment (baked into the app at build time)
+2. **CLI flags or `MRT_*` environment variables** — Control push/deploy targets
+3. **`.env` files** — Local development only (not deployed)
+
+### MRT Deployment Variables
+
+```bash
+# Project slug (required for push)
+MRT_PROJECT=my-project-slug
+
+# Target environment (optional — if omitted, bundle is uploaded but not deployed)
+MRT_TARGET=development
+```
+
+### Application Variables (set in MRT Dashboard)
+
+```bash
+PUBLIC__app__commerce__api__clientId=prod-client-id
+PUBLIC__app__commerce__api__organizationId=prod-org-id
+PUBLIC__app__commerce__api__shortCode=prod-short-code
+```
+
+## Page Designer Cartridge Deployment
+
+Page Designer metadata must be deployed separately to Commerce Cloud (not MRT):
+
+```bash
+# Generate cartridge metadata
+pnpm generate:cartridge
+
+# Deploy cartridge to B2C instance
+pnpm deploy:cartridge
+
+# Deploy with clean (removes old cartridge first)
+pnpm deploy:cartridge:clean
+
+# Validate cartridge structure
+pnpm validate:cartridge
+```
+
+Cartridge metadata is also auto-generated as part of `pnpm build`.
+
+## Pre-Deployment Checklist
+
+1. **Run tests** — `pnpm test`
+2. **Check bundle size** — `pnpm bundlesize:test`
+3. **Verify environment variables** — All required vars set in target environment
+4. **Build successfully** — `pnpm build` completes without errors
+5. **Verify SCAPI credentials** — Client ID and org ID match the target environment
+
+## Troubleshooting
+
+| Issue | Cause | Solution |
+| ------------------------------ | ----------------------------- | ------------------------------------------- |
+| Build fails | TypeScript errors | Fix type errors; run `pnpm typecheck` |
+| Push rejected | Authentication issue | Verify sfnext CLI credentials |
+| 500 errors after deploy | Missing environment variables | Check all required vars in MRT dashboard |
+| Stale Page Designer components | Cartridge not deployed | Re-deploy cartridge via MCP tool or b2c CLI |
+
+## Related Skills
+
+- `storefront-next:sfnext-project-setup` - Project structure and build configuration
+- `storefront-next:sfnext-configuration` - Environment variable configuration
+- `storefront-next:sfnext-page-designer` - Page Designer cartridge deployment
+- `storefront-next:sfnext-performance` - Bundle size optimization before deployment
+- `b2c-cli:b2c-mrt` - General MRT management via b2c CLI (NOT Storefront Next specific)
+
+## Reference Documentation
+
+- [MRT Deployment Reference](references/MRT-DEPLOYMENT.md) - Detailed deployment options and configuration
diff --git a/skills/storefront-next/skills/sfnext-deployment/references/MRT-DEPLOYMENT.md b/skills/storefront-next/skills/sfnext-deployment/references/MRT-DEPLOYMENT.md
new file mode 100644
index 00000000..2d0cebdb
--- /dev/null
+++ b/skills/storefront-next/skills/sfnext-deployment/references/MRT-DEPLOYMENT.md
@@ -0,0 +1,92 @@
+# MRT Deployment Reference
+
+## Managed Runtime (MRT)
+
+MRT is the hosting platform for Storefront Next storefronts. It provides:
+
+- **Server-side rendering** — Node.js runtime for SSR and loader execution
+- **CDN** — Global content delivery for static assets
+- **Environment management** — Separate environments for development, staging, production
+- **Bundle management** — Versioned deployments with rollback capability
+
+## Deployment Commands
+
+```bash
+# Build and push in one step
+pnpm build && pnpm push
+
+# Push with deployment message
+pnpm sfnext push -m "Fix checkout flow"
+
+# Push to specific environment
+pnpm sfnext push --environment production --wait
+
+# Create a bundle without deploying (inspection/custom pipelines)
+pnpm sfnext create-bundle -d . -o .bundle
+```
+
+## Environment Variables on MRT
+
+### Setting Variables
+
+Environment variables are set per-environment through:
+
+1. **MRT Dashboard** — UI for managing environment variables
+2. **CLI/.env values** — `SFCC_MRT_*` values used by `pnpm sfnext push`
+
+### Variable Limits
+
+| Constraint | Limit |
+| ----------------------- | ------------------ |
+| Variable name length | 512 characters max |
+| Total `PUBLIC__` values | 32KB max |
+| Nesting depth | 10 levels max |
+
+### Production Configuration Example
+
+```bash
+# Commerce API credentials
+PUBLIC__app__commerce__api__clientId=prod-client-id
+PUBLIC__app__commerce__api__organizationId=f_ecom_abcd_001
+PUBLIC__app__commerce__api__siteId=RefArchGlobal
+PUBLIC__app__commerce__api__shortCode=kv7kzm78
+
+# Site configuration
+PUBLIC__app__defaultSiteId=RefArchGlobal
+PUBLIC__app__commerce__sites='[{"id":"RefArchGlobal","defaultLocale":"en-US","defaultCurrency":"USD","supportedLocales":[{"id":"en-US","preferredCurrency":"USD"},{"id":"de-DE","preferredCurrency":"EUR"}],"supportedCurrencies":["USD","EUR"]}]'
+
+# Server-only secrets (not exposed to client)
+COMMERCE_API_SLAS_SECRET=production-slas-secret
+```
+
+## Bundle Management
+
+Each `sfnext push` creates a versioned bundle on MRT:
+
+```
+Bundle v1 (active) ← current production
+Bundle v2 ← previous deployment
+Bundle v3 ← two deployments ago
+```
+
+### Rollback
+
+If a deployment causes issues, roll back to a previous bundle via the MRT Dashboard or CLI.
+
+## Multi-Environment Setup
+
+| Environment | Purpose | Auto-deploy |
+| ----------- | ------------------------- | --------------------- |
+| Development | Feature testing | From feature branches |
+| Staging | Pre-production validation | From main branch |
+| Production | Live storefront | Manual promotion |
+
+## Deployment Verification
+
+After deploying, verify:
+
+1. **Health check** — Site loads without errors
+2. **SCAPI connectivity** — Products and categories display correctly
+3. **Authentication** — Login/logout flow works
+4. **Page Designer** — Merchant-editable pages render correctly
+5. **Performance** — No regression in page load times
diff --git a/skills/storefront-next/skills/sfnext-extensions/SKILL.md b/skills/storefront-next/skills/sfnext-extensions/SKILL.md
new file mode 100644
index 00000000..b2cb674a
--- /dev/null
+++ b/skills/storefront-next/skills/sfnext-extensions/SKILL.md
@@ -0,0 +1,116 @@
+---
+name: sfnext-extensions
+description: Build extensions for Storefront Next using target-config.json, target points, extension routes, and translation namespaces. Use when creating modular features, inserting components into UI targets, adding extension routes, or using SFDC_EXT_ integration markers. Covers extension structure, targetId configuration, and extension registration in src/extensions/config.json.
+---
+
+# Extensions Skill
+
+This skill covers the Storefront Next extension system — modular features that plug into the storefront via target points.
+
+## Overview
+
+Extensions are self-contained feature modules that add components, routes, translations, and providers to a Storefront Next storefront without modifying core code.
+
+## Extension Structure
+
+```
+src/extensions/my-extension/
+├── target-config.json # Target configuration (components/providers)
+├── components/ # Extension components
+├── routes/ # Extension routes (auto-registered)
+├── locales/ # Extension translations (auto-namespaced)
+└── providers/ # Extension context providers
+```
+
+## Target Configuration
+
+The `target-config.json` file declares how the extension integrates:
+
+```json
+{
+ "components": [
+ {
+ "targetId": "header.before.cart",
+ "path": "extensions/my-extension/components/badge.tsx",
+ "order": 0
+ }
+ ],
+ "contextProviders": [
+ {
+ "path": "extensions/my-extension/providers/my-provider.tsx",
+ "order": 0
+ }
+ ]
+}
+```
+
+### Target Points
+
+Target points are named insertion slots in the storefront layout (for example, `header.before.cart`). Extensions insert components at these points via `targetId`.
+
+Extension availability is managed in `src/extensions/config.json`, where each extension is keyed by an `SFDC_EXT_*` marker.
+
+## Extension Routes
+
+Files in the `routes/` directory auto-register as routes:
+
+```typescript
+// src/extensions/my-extension/routes/my-route.tsx
+export function loader() {
+ return { message: 'Hello' };
+}
+
+export default function MyRoute() {
+ const { message } = useLoaderData();
+ return
{message}
;
+}
+```
+
+## Extension Translations
+
+Translations are auto-namespaced as `extPascalCase` based on the directory name:
+
+```
+src/extensions/my-extension/locales/
+├── en-US/translations.json
+└── it-IT/translations.json
+```
+
+```typescript
+const {t} = useTranslation('extMyExtension');
+t('welcome');
+```
+
+## Integration Markers
+
+Mark extension integration points in core code:
+
+```typescript
+// Single line marker
+/** @sfdc-extension-line SFDC_EXT_MY_FEATURE */
+import myFeature from '@extensions/my-feature';
+
+// Block marker
+{/* @sfdc-extension-block-start SFDC_EXT_MY_FEATURE */}
+My Feature
+{/* @sfdc-extension-block-end SFDC_EXT_MY_FEATURE */}
+```
+
+See [Extension Examples Reference](references/EXTENSION-EXAMPLES.md) for complete examples.
+
+## Best Practices
+
+1. **Self-contained** — Each extension should be independent
+2. **Use target points** — Insert UI via `target-config.json` rather than editing core files
+3. **Namespace translations** — Auto-namespacing prevents collisions
+4. **Order matters** — Use `order` in `target-config.json` to control rendering order
+
+## Related Skills
+
+- `storefront-next:sfnext-i18n` - Translation patterns and namespace usage
+- `storefront-next:sfnext-routing` - How extension routes integrate with the router
+- `storefront-next:sfnext-components` - Component patterns used in extensions
+
+## Reference Documentation
+
+- [Extension Examples Reference](references/EXTENSION-EXAMPLES.md) - Complete extension examples
diff --git a/skills/storefront-next/skills/sfnext-extensions/references/EXTENSION-EXAMPLES.md b/skills/storefront-next/skills/sfnext-extensions/references/EXTENSION-EXAMPLES.md
new file mode 100644
index 00000000..7bcaed76
--- /dev/null
+++ b/skills/storefront-next/skills/sfnext-extensions/references/EXTENSION-EXAMPLES.md
@@ -0,0 +1,116 @@
+# Extension Examples Reference
+
+## Store Locator Extension
+
+A complete extension adding store locator functionality:
+
+```
+src/extensions/store-locator/
+├── target-config.json
+├── components/
+│ ├── store-locator-badge.tsx # Badge in header
+│ └── store-locator-map.tsx # Map component
+├── routes/
+│ └── store-locator.tsx # /store-locator page
+├── locales/
+│ ├── en-US/translations.json
+│ └── de-DE/translations.json
+└── providers/
+ └── store-provider.tsx # Store data context
+```
+
+### target-config.json
+
+```json
+{
+ "components": [
+ {
+ "targetId": "header.after.logo",
+ "path": "extensions/store-locator/components/store-locator-badge.tsx",
+ "order": 0
+ }
+ ],
+ "contextProviders": [
+ {
+ "path": "extensions/store-locator/providers/store-provider.tsx",
+ "order": 0
+ }
+ ]
+}
+```
+
+### Route
+
+```typescript
+// src/extensions/store-locator/routes/store-locator.tsx
+import { createApiClients } from '@/lib/api-clients';
+import { useTranslation } from 'react-i18next';
+
+export function loader({ context }: LoaderFunctionArgs) {
+ const clients = createApiClients(context);
+ return {
+ stores: clients.shopperStores.getStores({...}).then(({ data }) => data),
+ };
+}
+
+export default function StoreLocatorPage({ loaderData }) {
+ const { t } = useTranslation('extStoreLocator');
+
+ return (
+