# Extension System — Slot/Fill, Page Overrides, Hooks

> **Reference for `application` skill** — how a client project enriches a SmartStack page without forking the SDK.

## When This Reference Applies

- Client wants to inject content into an existing SmartStack page (header action, table column, side panel)
- Client wants to fully replace a built-in page with a custom one
- Client needs to react to navigation/auth/tenant lifecycle events
- The user asks "how do I add a column to UsersList", "how do I override the AdministrationDashboard page", "how do I customize a SmartStack form"

---

## Three extension mechanisms

### 1. Page overrides — `componentRegistry` + `PageRegistry`

Override a built-in page by registering your component under the same key :

```tsx
// In your client componentRegistry.generated.ts (or a custom registry.ts)
import { PageRegistry } from '@atlashub/smartstack';
import { lazy } from 'react';

PageRegistry.register(
  'administration.users',                       // built-in key — replaces SmartStack page
  lazy(() => import('@/pages/MyCustomUsers')),  // your component
);

// Or via SmartStackConfig.extensions.pages (declarative)
const config: SmartStackConfig = {
  apiUrl: '...',
  extensions: {
    pages: {
      'administration.users': () => import('@/pages/MyCustomUsers'),
    },
  },
};
```

The `DynamicRouter` calls `PageRegistry.resolve(componentKey)` and your override wins.

> **Tip** — for net-new pages on client-defined routes, prefer the MCP tool `mcp__smartstack__scaffold_routes` to keep `componentRegistry.generated.ts` consistent.

### 2. Slot / Fill — render-prop injection

Inject content into a named slot exposed by a SmartStack page :

```tsx
import { Fill } from '@atlashub/smartstack';

// Inject into the slot 'users.header.actions'
<Fill slot="users.header.actions">
  <button onClick={...}>My custom action</button>
</Fill>
```

Available slot names follow the convention `{section}.{location}.{purpose}` (see `extensions/types.ts` SlotName union for the full list — examples : `users.header.actions`, `users.table.columns.before`, `users.table.row.actions`, `users.form.fields.after`, `users.detail.tabs.after`).

You can declare slots once via `SmartStackConfig.extensions.slots` :

```ts
extensions: {
  slots: {
    'users.header.actions': MyHeaderWidget,
    'dashboard.kpi.before': MyKpi,
  }
}
```

### 3. Lifecycle hooks

Subscribe to platform events from anywhere :

```ts
extensions: {
  hooks: {
    onAuthSuccess: (user) => analytics.identify(user.id),
    onTenantSwitch: (tenant) => router.navigate('/dashboard'),
    onNavigationChanged: (menu) => console.log('menu refreshed'),
  }
}
```

---

## Table columns & form fields extensions

Extend the standard `DataTable` or `EntityForm` of any built-in section :

```ts
extensions: {
  tableColumns: {
    'administration.users.list': [
      {
        key: 'department',
        label: 'Department',
        render: (row) => row.department?.name,
        align: 'left',
        sortable: true,
      },
    ],
  },
  formFields: {
    'administration.users.create': [
      {
        name: 'department',
        type: 'select',
        label: 'Department',
        options: () => departmentApi.list(),
        validation: { required: true },
      },
    ],
  },
}
```

These extensions are consumed by the corresponding hooks in the SmartStack page :

```tsx
const extraColumns = useTableColumnExtensions('administration.users.list');
const extraFields = useFormFieldExtensions('administration.users.create');
```

---

## DO / DON'T

| ✅ DO | ❌ DON'T |
|---|---|
| Use `PageRegistry.register()` once at startup | Register the same key twice (last wins, but ambiguous) |
| Use lazy imports for page overrides | Import override pages eagerly (kills code splitting) |
| Match exact componentKey from the navigation menu | Guess the key — read it from `/api/navigation/menu` response |
| Use slots for additive content | Use slots to remove content (use page override instead) |
| Type custom column data via `row` typing | Cast `row as any` |

---

## MCP delegation

For complex extension scaffolds (page override + slots + tests), prefer the MCP tool over hand-written code :

| Goal | MCP tool |
|---|---|
| Generate a page override + register it | `mcp__smartstack__scaffold_frontend_extension` (type: `page-override`) |
| Generate a custom Slot component | `mcp__smartstack__scaffold_frontend_extension` (type: `slot`) |
| Generate a custom column extension | `mcp__smartstack__scaffold_frontend_extension` (type: `table-column`) |

---

## Reference source files (read-only)

- `D:\01 - projets\SmartStack.app\features\IA-Workflow\web\smartstack-web\src\extensions\PageRegistry.tsx`
- `D:\01 - projets\SmartStack.app\features\IA-Workflow\web\smartstack-web\src\extensions\types.ts` (SlotName, ColumnExtension, FormFieldExtension)
- `D:\01 - projets\SmartStack.app\features\IA-Workflow\web\smartstack-web\src\extensions\ExtensionContext.tsx` (useExtensions, useTableColumnExtensions, useFormFieldExtensions)
- `D:\01 - projets\SmartStack.app\features\IA-Workflow\web\smartstack-web\src\extensions\SlotFill.tsx` (Slot, Fill, SlotFillProvider)

See also [smartstack-provider.md](smartstack-provider.md) for the SmartStackConfig shape that hosts `extensions`.
