# Pcf

> PCF (Power Apps Component Framework) control development. Use when: building PCF controls, ReactControl, dataset controls, manifest patterns, Fluent UI v9 integration, Canvas App deployment, debugging PCF.

- **Type:** Skill
- **Install:** `agentstack add skill-korchard333-claude-power-platform-community-pcf`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [korchard333](https://agentstack.voostack.com/s/korchard333)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [korchard333](https://github.com/korchard333)
- **Source:** https://github.com/korchard333/claude-power-platform-community/tree/main/.claude/skills/pcf

## Install

```sh
agentstack add skill-korchard333-claude-power-platform-community-pcf
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Skill: PCF (Power Apps Component Framework)

## When to Use
Trigger when building, debugging, or reviewing PCF controls — TypeScript React components for Model-Driven or Canvas Apps.

---

## Project Structure

```
MyControl/
├── MyControl/
│   ├── index.ts              # Main control class (StandardControl or ReactControl)
│   ├── MyComponent.tsx       # React component
│   ├── css/styles.css        # Component styles
│   ├── strings/              # Resx localization files
│   └── generated/
│       └── ManifestTypes.d.ts  # Auto-generated from manifest
├── ControlManifest.Input.xml # Component manifest (properties, resources)
├── MyControl.pcfproj         # Project file
├── package.json
├── tsconfig.json
└── solutions/
    └── Solution.cdsproj      # Solution project for deployment
```

---

## Control Types

### ReactControl (Virtual — Recommended for New Development)
```typescript
// No DOM container — React manages rendering
export class MyControl implements ComponentFramework.ReactControl {
  private notifyOutputChanged: () => void;

  public init(context: ComponentFramework.Context, notifyOutputChanged: () => void): void {
    this.notifyOutputChanged = notifyOutputChanged;
  }

  public updateView(context: ComponentFramework.Context): React.ReactElement {
    return React.createElement(MyComponent, {
      value: context.parameters.sampleProperty.raw ?? "",
      disabled: context.mode.isControlDisabled,
      onChange: (val: string) => {
        this._value = val;
        this.notifyOutputChanged();
      },
    });
  }

  public getOutputs(): IOutputs {
    return { sampleProperty: this._value };
  }

  public destroy(): void { /* cleanup */ }
  private _value: string = "";
}
```

### StandardControl (Legacy — Has DOM Container)
```typescript
// Gets a container div for direct DOM manipulation
export class MyControl implements ComponentFramework.StandardControl {
  private container: HTMLDivElement;

  public init(context, notifyOutputChanged, state, container: HTMLDivElement): void {
    this.container = container;
  }

  public updateView(context): void {
    ReactDOM.render(React.createElement(MyComponent, props), this.container);
  }

  public destroy(): void {
    ReactDOM.unmountComponentAtNode(this.container);
  }
}
```

---

## Manifest Patterns

### Field Control

> ⚠️ **CRITICAL:** Virtual controls using React/Fluent MUST declare `` elements. Without these, the PCF bundles its own React — and the platform ALSO provides React — resulting in two React instances. Fluent UI v9 hooks immediately fail with **"Invalid hook call… more than one copy of React"** and bundle size bloats from ~16 KB to ~2.5 MB.

```xml

  
  
    
    
    
    
    
  

```

**With platform-library declarations:**
- Move `@fluentui/react-components`, `react`, and `react-dom` to `devDependencies` (types only at compile time)
- `bundle.js` drops from ~2.5 MB → ~16 KB
- The platform provides one shared React instance to all virtual controls on the page

**Official docs:** [platform-library element reference](https://learn.microsoft.com/power-apps/developer/component-framework/manifest-schema-reference/platform-library) — this is Category B, fully documented.

### Dataset Control
```xml

  
  

```

---

## Dataset Paging Pattern

```typescript
public updateView(context: ComponentFramework.Context): React.ReactElement {
  const dataset = context.parameters.dataSetGrid;

  if (dataset.loading) return React.createElement("div", null, "Loading...");

  const records = dataset.sortedRecordIds.map((id) => {
    const record = dataset.records[id];
    return {
      id,
      name: record.getFormattedValue("name"),
      status: record.getFormattedValue("statuscode"),
    };
  });

  const hasMore = dataset.paging.hasNextPage;
  const loadMore = () => dataset.paging.loadNextPage();

  return React.createElement(MyGrid, { records, hasMore, loadMore });
}
```

---

## Fluent UI v9 Integration

```typescript
// PERFORMANCE: Import guidance for Fluent UI v9
// The barrel import tree-shakes correctly with modern bundlers (webpack 5+, esbuild):
import { Button, Input, Label } from "@fluentui/react-components"; // OK with tree-shaking

// For smaller bundles, use individual package imports:
import { Button } from "@fluentui/react-button";
import { Input } from "@fluentui/react-input";
// Check package availability — not all components have separate packages

// Wrap in FluentProvider for consistent theming
public updateView(context): React.ReactElement {
  return React.createElement(
    FluentProvider,
    { theme: webLightTheme },
    React.createElement(MyComponent, props)
  );
}
```

## React Platform Library Versions

> Last verified: March 2026. Check [Microsoft Learn](https://learn.microsoft.com/power-apps/developer/component-framework/overview) for current versions.

> **IMPORTANT:** The PCF platform library does **NOT** use React 18. The actual versions are:

| Context | React Version | Notes |
|---|---|---|
| **Build (bundled)** | **16.14.0** | Version used at compile time |
| **Model-Driven App runtime** | **17.0.2** | Provided by the MDA shell at runtime |
| **Canvas App runtime** | **16.14.0** | Provided by the Canvas runtime |

**Do NOT use React 18-specific APIs** such as `useTransition`, `useDeferredValue`, `useSyncExternalStore`, or concurrent rendering features — they are not available in the platform library.

### Fluent UI v9 Version Constraints

| Context | Fluent UI v9 Version |
|---|---|
| **Build (max)** | **≤ 9.46.2** |
| **Runtime (provided)** | **9.68.0** |

Do not install Fluent UI v9 packages above 9.46.2 in your project — the build will succeed but runtime mismatches may cause unexpected behavior.

### ReactControl (Virtual — Recommended)

**ReactControl** (virtual, `control-type="virtual"`) uses the platform-provided React automatically — no manual `createRoot` needed. The `control-type="virtual"` attribute is **required** for React controls. This is the recommended approach for all new controls.

### StandardControl (Legacy — Has DOM Container)

For StandardControl using React, use `ReactDOM.render` / `ReactDOM.unmountComponentAtNode` (matching the React 16/17 API):

```typescript
import * as ReactDOM from "react-dom";

export class MyControl implements ComponentFramework.StandardControl {
  private container: HTMLDivElement;

  public init(context, notifyOutputChanged, state, container: HTMLDivElement): void {
    this.container = container;
  }

  public updateView(context): void {
    ReactDOM.render(React.createElement(MyComponent, props), this.container);
  }

  public destroy(): void {
    ReactDOM.unmountComponentAtNode(this.container);
  }
}
```

**Recommendation:** Always prefer `ReactControl` (virtual) — it handles React lifecycle correctly and avoids DOM container issues.

### Platform Support Limitations

- **PCF controls are NOT supported in Power Pages** — only Model-Driven Apps and Canvas Apps
- When using platform libraries, do **not** bundle React or Fluent UI as externals — this causes double-loading

---

## ESLint Setup for PCF

`pac pcf push` runs `npm install` internally. Extra ESLint devDependencies cause ERESOLVE failures due to incompatible peer requirements.

**Use minimal ESLint only:**

```json
// package.json — devDependencies
"eslint": "^9.0.0"
// DO NOT add: typescript-eslint, eslint-plugin-promise, @typescript-eslint/eslint-plugin, eslint-plugin-react
// These have incompatible peer requirements that cause pac pcf push to fail
```

```js
// eslint.config.mjs — minimal config that satisfies pcf-scripts peer dep
export default [
  { ignores: ["node_modules/**", "out/**", "obj/**", "generated/**"] }
];
```

No TypeScript-ESLint, no eslint-plugin-react, no globals package needed. pcf-scripts only needs ESLint as a peer dep.

## `scheduler` Dependency

`@fluentui/react-context-selector` imports `scheduler` directly. npm may install it nested inside `react-dom/node_modules/` rather than hoisted to the project root. If webpack reports "Module not found: Can't resolve 'scheduler'", add it explicitly:

```json
"scheduler": "^0.20.2"
```

Must be `^0.20.2` — `@fluentui/react-components@9.46.2` requires `^0.19.0 || ^0.20.0`; version `0.27.x` causes peer conflict during `pac pcf push`.

---

## CLI Workflow

```bash
# Create new control
pac pcf init --namespace Contoso --name MyControl --template field --framework react

# Install dependencies
npm install

# Local development with test harness
npm start watch    # Opens http://localhost:8181

# Build
npm run build

# Create solution project
pac solution init --publisher-name Contoso --publisher-prefix contoso
pac solution add-reference --path ../MyControl

# Build solution
dotnet build    # or msbuild /t:build /restore

# Push to environment (dev only)
pac pcf push --publisher-prefix contoso
```

---

## Best Practices

- Always implement `destroy()` — clean up event listeners, timers, subscriptions
- Always handle `context.mode.isControlDisabled` — disable inputs when form is read-only
- Always handle `context.mode.isVisible` — skip rendering when hidden
- Diff props in `updateView` before re-rendering to avoid flicker
- Use `control-type="virtual"` for React controls (required for platform library React)
- Never call `notifyOutputChanged()` inside `updateView()` — causes infinite loops
- Use `context.webAPI` for Dataverse operations (respects user security context)
- Use `context.navigation` for dialog/form opening
- Always specify `required-feature-set` in manifest when using WebAPI

---

## Debugging

1. **Test Harness**: `npm start watch` → `http://localhost:8181`
2. **In Environment**: Browser DevTools → Sources → webpack:// → find your .tsx files
3. **Fiddler/DevTools Network**: Capture `api/data/v9.2` calls for dataset debugging
4. Check `context.parameters.xxx.errorMessage` for binding errors
5. Check `context.parameters.xxx.security` for field-level security restrictions

---

## Canvas App Deployment

PCF controls can be used in Canvas Apps, but require additional manifest configuration.

### Enable for Canvas Apps
```xml

  

  
  
    
    
  

  
    
  

```

### Canvas App vs Model-Driven App Differences
| Feature | Model-Driven App | Canvas App |
|---|---|---|
| `context.webAPI` | Available | Available (if declared in feature-usage) |
| `context.navigation` | Full support | Limited (no openForm) |
| `context.mode.isControlDisabled` | Managed by form | Managed by app maker |
| Dataset controls | Full support | Supported (with limitations) |
| Theming | Inherits MDA theme | Inherits Canvas theme |
| `notifyOutputChanged()` | Triggers form dirty | Triggers OnChange in Canvas |

### Import to Canvas App
```
Canvas App → Insert → Get more components → Code tab → Import → Select your PCF control
```

---

## Anti-Patterns

- Calling `notifyOutputChanged()` in `updateView()` (infinite render loop)
- Not implementing `destroy()` (memory leaks, stale listeners)
- Ignoring `isControlDisabled` (control remains editable on read-only forms)
- Using `any` types for context parameters
- Hardcoding entity names or field names (use manifest properties)
- Not handling loading state for dataset controls
- Using React 18-specific APIs (`useTransition`, `useDeferredValue`, concurrent features) — platform provides React 16.14.0/17.0.2
- **Missing `` declarations** in virtual control manifest — causes two React instances, "Invalid hook call" error, and ~2.5 MB bundle instead of ~16 KB. Always declare both `React` and `Fluent` platform libraries for virtual controls using Fluent UI v9.

---

## Sub-Files

- **[PCF API Reference](api-reference.md)** — Context object properties, context.mode, context.webAPI methods, context.navigation, context.device, context.formatting, DataSet API (columns/filtering/paging/records), EntityRecord methods, custom events, platform availability matrix (MDA vs Canvas vs Portals)

---

## Related Skills

- `code-apps` — React/TypeScript patterns shared with PCF development
- `accessibility-ux` — WCAG compliance requirements for PCF controls

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [korchard333](https://github.com/korchard333)
- **Source:** [korchard333/claude-power-platform-community](https://github.com/korchard333/claude-power-platform-community)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-korchard333-claude-power-platform-community-pcf
- Seller: https://agentstack.voostack.com/s/korchard333
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
