Pcf logo

Pcf

Community
korchard333
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.

Overview

Publisherkorchard333
Repositoryclaude-power-platform-community
Skill namepcf
Stars
50
Forks
6
Bundled files
3
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 3 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by korchard333 on GitHub. Read the source before you install it.

Installation

Install the Pcf AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/korchard333/claude-power-platform-community/tree/main/.claude/skills/pcf
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/korchard333/claude-power-platform-community.git /tmp/claude-power-platform-community
mkdir -p .claude/skills
cp -r /tmp/claude-power-platform-community/.claude/skills/pcf .claude/skills/pcf
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Pcf in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Pcf on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Pcf is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

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<IInputs, IOutputs> {
  private notifyOutputChanged: () => void;

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

  public updateView(context: ComponentFramework.Context<IInputs>): 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<IInputs, IOutputs> {
  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 <platform-library> 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
<control namespace="Contoso" constructor="MyControl" version="1.0.0"
         display-name-key="MyControl" description-key="MyControl_Desc"
         control-type="virtual">
  <property name="sampleProperty"
            display-name-key="Property_Display"
            description-key="Property_Desc"
            of-type="SingleLine.Text"
            usage="bound"
            required="true" />
  <resources>
    <code path="index.ts" order="1" />
    <css path="css/styles.css" order="1" />
    <!-- REQUIRED for virtual controls using React/Fluent UI:
         These declarations tell webpack to exclude React and Fluent from the bundle.
         The platform provides one shared instance at runtime.
         Omitting these causes double-React loading → "Invalid hook call" error. -->
    <platform-library name="React" version="16.14.0" />
    <platform-library name="Fluent" version="9.46.2" />
  </resources>
</control>

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 — this is Category B, fully documented.

Dataset Control

xml
<control ... control-type="virtual">
  <data-set name="dataSetGrid"
            display-name-key="DataSet_Display"
            cds-data-set-options="displayCommandBar:true;displayViewSelector:true">
  </data-set>
</control>

Dataset Paging Pattern

typescript
public updateView(context: ComponentFramework.Context<IInputs>): 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 for current versions.

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

ContextReact VersionNotes
Build (bundled)16.14.0Version used at compile time
Model-Driven App runtime17.0.2Provided by the MDA shell at runtime
Canvas App runtime16.14.0Provided 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

ContextFluent 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<IInputs, IOutputs> {
  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
<!-- In ControlManifest.Input.xml — add feature-usage section -->
<control ... control-type="virtual">
  <property ... />

  <!-- Required for Canvas App support -->
  <feature-usage>
    <uses-feature name="utility" required="true" />
    <uses-feature name="WebAPI" required="true" />
  </feature-usage>

  <resources>
    <code path="index.ts" order="1" />
  </resources>
</control>

Canvas App vs Model-Driven App Differences

FeatureModel-Driven AppCanvas App
context.webAPIAvailableAvailable (if declared in feature-usage)
context.navigationFull supportLimited (no openForm)
context.mode.isControlDisabledManaged by formManaged by app maker
Dataset controlsFull supportSupported (with limitations)
ThemingInherits MDA themeInherits Canvas theme
notifyOutputChanged()Triggers form dirtyTriggers 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 <platform-library> 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 — 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

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Pcf AI skill do?

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.

Why use Pcf on TypingMind?

Because you install it once and use it with any model. Pcf is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Pcf in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/korchard333/claude-power-platform-community/tree/main/.claude/skills/pcf. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Pcf?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Pcf?

As many as you like. As long as a model supports skills, you can use Pcf with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Pcf AI skill free?

Yes. It is published on GitHub by korchard333 under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇