# Dynamic Context — Widget Generation Guide

> ⚠️ **First action for any widget work: invoke the `widgets-init-project` MCP Prompt.**
> It instructs the agent to invoke the `widgets-scaffold` Tool, which clones the
> canonical template at the pinned version declared in `WIDGETS_COMPAT`
> (`src/tools/widgets/_compatibility.ts` — the single source for the pinned
> Dynamic UI / template / validator tuple; values are deliberately not
> duplicated here).
> Generating from memory will diverge from this matrix — typically falling back to
> a Dynamic UI 1.x stack that is over a year behind. The validator's rules are
> universal but calibrated to the matrix, so a divergent stack can score 100/100
> while being structurally wrong. The clone is the only path to reproducible builds.
>
> For existing widget projects, verify the matrix in `package.json` against
> `WIDGETS_COMPAT` before suggesting changes — if the major of
> `@dynamic-framework/ui-react` diverges from `WIDGETS_COMPAT.dynamicUi`, the
> project is on a legacy stack and patterns from this knowledge base may not apply.

> **For AI:** This is the single entry point. Read this file first, then follow links as needed.
> **Do not read all documentation at once.** Load selectively based on the widget you're building.

---

## Stack (Version-Locked)

| Dependency | Version | Purpose |
|------------|---------|---------|
| React | 19 | UI framework |
| TypeScript | 5.7+ | Type safety |
| @dynamic-framework/ui-react | 2.8.0 | Component library |
| @tanstack/react-query | 5.x | Server state (data fetching) |
| zustand | 5.x | UI state management |
| i18next | 25.x | Internationalization |
| lucide-react | 0.553+ | Icons (PascalCase names) |
| Vite | 7.x | Build tool |
| Vitest | 3.x | Testing |
| Axios | 1.9.0 | HTTP client |

**All versions are pinned in the canonical `dynamic-react-vite-base-template` (see Base Template section). Never modify them.**

---

## Plataforma Modyo

Este módulo del MCP cubre la generación del **proyecto de widget** (React + Dynamic UI). Para el contexto de plataforma Modyo (widget UUIDs vs IDs, CSP nonce, ciclo de vida en sites, edición de widgets publicados) ver:

| Documento | Cuándo leer |
|-----------|-------------|
| `modyo://docs/widgets/platform-development` | Best practices de desarrollo de widgets en sites Modyo |
| `modyo://docs/widgets/platform-workflows` | Workflows de creación/asignación de widgets |
| `modyo://docs/widgets/platform-editing-published` | Edición segura de widgets ya publicados |

---

## Critical Rules (Read Before Coding)

These rules prevent the most common errors. For full reference → `modyo://docs/widgets/reference-conventions`

### Props

| Rule | Correct | Wrong |
|------|---------|-------|
| Color prop | `<DButton color="primary" />` | `<DButton theme="primary" />` |
| Text as prop | `<DButton text="Save" />` | `<DButton>Save</DButton>` |
| Boolean props | `<DButton disabled />` | `<DButton isDisabled />` |
| Portal open | `openPortal('name', {})` | `openPortal('name', <JSX />)` |
| Badge soft style | `<DBadge soft />` | `<DBadge variant="soft" />` |

### Handlers

**ALL Dynamic UI inputs pass VALUES directly, NOT events.**

```tsx
// ✅ CORRECT
<DInput onChange={(value: string) => setValue(value)} />
<DInputSwitch onChange={(checked: boolean) => setChecked(checked)} />

// ❌ WRONG — no e.target in Dynamic UI
<DInput onChange={(e) => setValue(e.target.value)} />
```

### DSelect (react-select based)

```tsx
// ✅ CORRECT — value is option OBJECT, onChange receives option OBJECT
<DSelect
  value={options.find(opt => opt.value === selected)}
  onChange={(option: any) => setSelected(option?.value || '')}
  options={options}
/>

// ❌ WRONG — not a native select
<DSelect value={selected} onChange={(value) => setSelected(value)} />
```

### Icons

```tsx
// ✅ Lucide PascalCase
<DIcon icon="CreditCard" color="primary" size="24px" />

// ❌ Wrong format, wrong color method, wrong size unit
<DIcon icon="credit-card" className="text-primary" size="1.5rem" />
```

### Code Style

**Arrow functions required** (ESLint `prefer-arrow-functions`):

```tsx
// ✅ CORRECT
const MyComponent = () => { return <div />; };

// ❌ WRONG — ESLint error
function MyComponent() { return <div />; }
```

### Modal Registration

All modals must be registered in `DContextProvider.availablePortals` in `src/main.tsx`:

```tsx
<DContextProvider availablePortals={{ 'my-modal': MyModal }}>
  <App />
</DContextProvider>
```

### Layout

```tsx
// ✅ Widget root
<div className="container py-4">

// ❌ Wrong
<div className="container-fluid py-4">
```

---

## Base Template (MANDATORY First Step)

El template canonical vive en `https://github.com/dynamic-framework/dynamic-react-vite-base-template`. Para iniciar el proyecto desde el MCP, el operador invoca el Prompt `widgets-init-project`, cuyo único efecto es que el agente ejecute la Tool **`widgets-scaffold`**.

**Before ANY code generation**, `widgets-scaffold` ejecuta el init de forma determinista:

1. Clona el template al tag pineado por `WIDGETS_COMPAT.template` (`git clone --depth 1 --branch <tag>`) en `./<name>/` bajo el `targetDir` indicado. Nunca desde `master`.
2. Elimina `.git/` (detach de la historia del template).
3. Renombra `name` en `package.json` al nombre del proyecto.
4. Re-inicializa git (`git init`) y crea el commit inicial `chore: scaffold from dynamic-react-vite-base-template <tag>` con la identidad git del operador (sin `user.name`/`user.email` configurados, el repo queda inicializado sin commit — `gitInit: "initialized_no_commit"`). El commit va antes de `npm install`, capturando el scaffold pristino (`node_modules/` queda fuera por el `.gitignore` del template).
5. Corre `npm install` (si falla, el proyecto queda utilizable con `npm install` manual).

La Tool falla cerrada — sin tocar nada — si el directorio destino ya existe o si el destino está en cualquier punto dentro de un proyecto de widget existente (su raíz o cualquier subdirectorio: el guard asciende por los ancestros buscando `@dynamic-framework/ui-react` en las dependencias). También falla cerrada si algún `package.json` de la cadena existe pero no puede leerse o parsearse — podría ser el del widget envolvente. Nunca anidar widgets ni reinterpretar el nombre como feature de un widget existente.

**Flujo de dos pasos:** (1) scaffold vía la Tool; (2) el operador entrega la descripción funcional del widget en el siguiente turno. No generes código de widget antes de recibirla.

### What's Included (DO NOT recreate)

| File | Purpose |
|------|---------|
| `vite.config.ts` | Build configuration |
| `vitest.config.ts` | Test configuration |
| `tsconfig.json` | TypeScript strict mode |
| `eslint.config.js` | ESLint flat config |
| `package.json` | All dependencies (pinned versions) |
| `index.html` | Entry HTML with Jost font CDN |
| `src/providers/QueryProvider.tsx` | TanStack Query setup |
| `src/store/useUIStore.ts` | Zustand store structure |
| `src/utils/errorHandler.ts` | Error handler (default export) |
| `src/config/i18nConfig.ts` | i18next base setup |
| `src/config/liquidConfig.ts` | Liquid parser init |
| `src/config/widgetConfig.ts` | Widget name config |
| `tests/setup.ts` | Vitest setup |

### What You Generate

```
src/types/              ← Domain interfaces
src/services/mocks/     ← Mock data (toggle via Liquid)
src/services/repositories/ ← API calls (repository pattern)
src/hooks/              ← TanStack Query hooks
src/store/useUIStore.ts ← Update with widget-specific UI state
src/components/         ← React components
src/locales/            ← en.json, es.json translations
src/config/widgetConfig.ts ← Update widget name
```

---

## Architecture

Every widget follows this structure. Details → `modyo://docs/widgets/reference-architecture`

```
widget-name/
├── index.html                  # Vite entry (in root)
├── vite.config.ts
├── package.json
├── src/
│   ├── main.tsx                # Entry point
│   ├── App.tsx                 # Root component
│   ├── providers/
│   │   └── QueryProvider.tsx   # TanStack Query
│   ├── store/
│   │   └── useUIStore.ts       # Zustand (UI state only)
│   ├── services/
│   │   ├── clients/
│   │   │   └── apiClient.ts    # Axios instance
│   │   ├── repositories/       # Repository pattern
│   │   ├── hooks/              # TanStack Query hooks (deprecated location)
│   │   ├── mappers/            # API → Domain
│   │   └── mocks/data/         # Mock data
│   ├── hooks/                  # TanStack Query hooks + custom hooks
│   ├── components/             # React components
│   ├── types/                  # TypeScript interfaces
│   ├── utils/                  # errorHandler.ts (default export required)
│   ├── locales/                # en.json, es.json (identical keys)
│   └── config/                 # i18n, liquid, widget config
└── tests/
    └── setup.ts
```

**Key distinction:** `src/hooks/` for TanStack Query and custom hooks. `src/services/hooks/` is a deprecated location that still works.

---

## Generation Workflow

| Step | Action | Reference |
|------|--------|-----------|
| 1 | Scaffold from `dynamic-react-vite-base-template` via the `widgets-scaffold` Tool | See Base Template section above (entry point: Prompt `widgets-init-project`) |
| 2 | Define domain types in `src/types/` | — |
| 3 | Create repository + mock data | `modyo://docs/widgets/patterns-repository` |
| 4 | Create TanStack Query hooks in `src/hooks/` | `modyo://docs/widgets/patterns-tanstack-query` |
| 5 | Set up Zustand store for UI state | `modyo://docs/widgets/patterns-zustand` |
| 6 | Build UI components | `modyo://docs/widgets/components-_index` |
| 7 | Add i18n translations (en.json + es.json) | `modyo://docs/widgets/patterns-i18n` |
| 8 | Configure mock toggle via Liquid | `modyo://docs/widgets/patterns-liquid-environment` |
| 9 | Run validator — target ≥ 95% | See Validator section below |

**Use code snippets from pattern docs.** Copy and adapt — don't write from scratch.

---

## Component Reference

**Start here:** `modyo://docs/widgets/components-_index` — full catalog with props and examples.

### By Category

| Category | File | Key Components |
|----------|------|----------------|
| Inputs | `modyo://docs/widgets/components-inputs` | DInput, DInputCurrency, DInputSwitch ⚠️ |
| Inputs (Advanced) | `modyo://docs/widgets/components-inputs-advanced` | DInputMask, DInputCounter, DBoxFile |
| Selects | `modyo://docs/widgets/components-selects` | DSelect, DInputSelect ⚠️ |
| Buttons | `modyo://docs/widgets/components-buttons` | DButton, DButtonGroup |
| Feedback | `modyo://docs/widgets/components-feedback` | DAlert, DBadge, DToast, DSpinner |
| Layout | `modyo://docs/widgets/components-layout` | DCard, DBox, DCollapse |
| Navigation | `modyo://docs/widgets/components-navigation` | DTabs, DStepper, DPaginator |
| Modals | `modyo://docs/widgets/components-modals` | DModal, DOffcanvas ⚠️ |
| Overlay (Portals) | `modyo://docs/widgets/components-overlay` | DModal, DOffcanvas (portal guide) |
| Overlay (Widgets) | `modyo://docs/widgets/components-overlay-widgets` | DPopover, DTooltip |
| Icons | `modyo://docs/widgets/components-icons` | DIcon (Lucide PascalCase) ⚠️ |
| Charts | `modyo://docs/widgets/components-charts` | Recharts integration |
| Other | `modyo://docs/widgets/components-other` | DAvatar, DListGroup, DCurrencyText |
| Hooks | `modyo://docs/widgets/components-hooks` | useDPortalContext, useFormatCurrency |

**⚠️ = Critical patterns that cause errors if misunderstood. Read the marked sections before using.**

### Selective Loading by Widget Type

| Widget Type | Load These Component Files |
|-------------|---------------------------|
| **Form** | inputs.md, selects.md, buttons.md, date-time.md |
| **List/Dashboard** | layout.md, feedback.md, icons.md, other.md |
| **Wizard** | navigation.md, inputs.md, selects.md, buttons.md |
| **Modal/Dialog** | hooks.md, modals.md, buttons.md |

---

## Catalog Discovery

The MCP exposes the Dynamic UI component catalog and pattern stories as **versioned MCP Resources**, sourced from the official Storybook manifests at the CDN pinned by `WIDGETS_COMPAT.dynamicUi`. They complement the curated documentation above with live data — story snippet code, prop types, MDX content.

| URI | Shape | Use when |
|-----|-------|----------|
| `modyo://widgets/catalog/components` | Lightweight list (id, name, key, description, storyCount) | Discovering what's available |
| `modyo://widgets/catalog/components/{componentName}` | Full detail (stories with snippet code, props with TS types, JSDoc) | Need exact API surface or a working snippet |
| `modyo://widgets/catalog/patterns` | Lightweight pattern list (id, name, title) | Discovering composed UI examples |
| `modyo://widgets/catalog/patterns/{patternId}` | Full MDX content of a pattern story | Adapting an official pattern to a new widget |

Full guide — Tools vs Resources, when to use each, output shapes, error handling: `modyo://docs/widgets/_meta-catalog-discovery`.

### Pattern Discovery (required)

> ⚠️ **Before generating widget UI**, you must consult `modyo://widgets/catalog/patterns` to identify official patterns matching the request domain. If a pattern matches, read its full content from `modyo://widgets/catalog/patterns/{patternId}` and use it as the structural and visual basis. Adapting to the request's specifics is expected; ignoring an applicable pattern produces visually generic output that fails to reflect Dynamic Framework conventions.
>
> If no pattern matches, state this explicitly and proceed using only components from `modyo://widgets/catalog/components`.

---

## Patterns Reference

| Pattern | File | Description |
|---------|------|-------------|
| Repository | `modyo://docs/widgets/patterns-repository` | API calls with mock toggle, AbortSignal, error handling |
| TanStack Query | `modyo://docs/widgets/patterns-tanstack-query` | useQuery/useMutation hooks for server state |
| Zustand | `modyo://docs/widgets/patterns-zustand` | UI state store (filters, modals, selections) |
| Hooks | `modyo://docs/widgets/patterns-hooks` | Custom hook patterns and naming |
| i18n | `modyo://docs/widgets/patterns-i18n` | Internationalization with Liquid template support |
| Liquid Environment | `modyo://docs/widgets/patterns-liquid-environment` | Mock/API toggle, environment config |
| Data States | `modyo://docs/widgets/patterns-data-states` | Loading/Error/Empty/Success handling |
| Testing | `modyo://docs/widgets/patterns-testing` | Vitest setup, hook testing |
| API Clients | `modyo://docs/widgets/patterns-api-clients` | Axios client configuration |
| Utilities | `modyo://docs/widgets/patterns-utilities` | errorHandler, liquidParser, formatters |

---

## Reference Documents

| Document | File | When to Read |
|----------|------|--------------|
| UI Conventions | `modyo://docs/widgets/reference-conventions` | Before coding (color, variant, size, boolean props) |
| Architecture | `modyo://docs/widgets/reference-architecture` | Folder structure details, naming conventions |
| UI Patterns | `modyo://docs/widgets/reference-ui-patterns` | Building forms, lists, wizards, modals |
| Domain Patterns | `modyo://docs/widgets/reference-domain-patterns` | Master-detail, quick actions, card patterns, tables |
| Validator Rules | `modyo://docs/widgets/reference-validator-rules` | When validator score < 95% |
| Troubleshooting | `modyo://docs/widgets/reference-troubleshooting` | Build/runtime errors |
| Theming | `modyo://docs/widgets/reference-theming` | Client branding customization |
| Accessibility | `modyo://docs/widgets/reference-accessibility` | A11y guidelines |
| Generation Workflow | `modyo://docs/widgets/reference-generation-workflow` | Detailed step-by-step process |
| Quick Reference | `modyo://docs/widgets/reference-quick-reference` | Icon lookup, prop cheat sheet |

---

## Validator

Run after generating a widget. The validator ships as the NPM package `@modyo/widget-validator`; you can invoke it three ways:

**1. MCP tool (preferred when the Modyo MCP server is active):**

| Tool | Input | Notes |
|------|-------|-------|
| `widgets-validate` | `projectPath` (filesystem path) **or** `projectFiles` (array of `{path, content}`) | Exactly one of the two. Returns a `ValidationReport` (`score`, `passing`, `errors`, `warnings`). |

**2. CLI via `npx` (no MCP dependency):**

```bash
npx @modyo/widget-validator ~/Code/dynamic-2.0/generated-widgets/[widget-name]
```

**3. Automated on every `Write`/`Edit` (Claude Code only):** see `modyo://docs/widgets/_meta-integration-claude-code` for the `PostToolUse` hook snippet.

**Minimum score: 95%.** Fix errors, re-run. For rule details → `modyo://docs/widgets/reference-validator-rules`

**Known quirks** (false positives, workarounds) → `modyo://docs/widgets/_meta-validator-quirks`. Currently documents the v0.1 empty-directory constraint that affects `projectFiles` invocations.

---

## Deploy (outside the MCP)

The generation flow ends at validation plus a served smoke test (`npm run dev`). Deployment is NOT an MCP operation: it runs through CI from the widget's own git repository — the scaffold initializes the repo with an initial commit precisely to enable that path. A manual push is the developer's explicit `modyo-cli push` in their own shell, never the agent.

Credentials (`MODYO_ACCOUNT_URL`, `MODYO_TOKEN`, `MODYO_SITE_HOST`) live in the repo's CI secrets or the developer's local `.env` — the MCP process never reads them, and the token never enters the chat.

Full guide: `modyo://docs/widgets/_meta-publishing` — deploy path (CI from the repo), manual `modyo-cli push`, credentials policy.

---

## Quality Checklist

Before submitting a widget:

- [ ] Validator score ≥ 95%
- [ ] `npm run dev` runs without errors
- [ ] All features work (no placeholders)
- [ ] Mock data toggle works (`use-mocks` in `liquid.json`)
- [ ] i18n keys present (en.json + es.json, identical structure)
- [ ] Modals registered in `availablePortals`
- [ ] Uses `<div className="container py-4">` as root

---

## External Links

- **Storybook:** https://react.dynamicframework.dev/
- **Lucide Icons:** https://lucide.dev/icons/
- **Dynamic UI Repo:** https://github.com/dynamic-framework/dynamic-ui
- **Base Template Repo:** https://github.com/dynamic-framework/dynamic-react-vite-base-template

---

**Last Updated:** May 2026
