4.4 PCF Fundamentals: The Component Manifest & Lifecycle Events
Key Takeaways
- pac pcf init --template field|dataset scaffolds a component; field binds to one table column, dataset binds to a view or subgrid's records and columns.
- ControlManifest.Input.xml declares the control, its bindable properties or data-set, its resources (code/CSS/RESX), and any feature-usage capabilities it needs at runtime.
- A component must declare <feature-usage> for WebAPI, Utility, or Device capabilities before it can actually use the corresponding context API.
- The lifecycle runs init() once, then updateView() repeatedly as data or size changes, getOutputs() to return pending changes, and destroy() once on removal.
- Choosing field vs. dataset at scaffolding time determines the entire shape of the manifest and the component's data-access code.
The Power Apps Component Framework (PCF) lets pro developers build custom, reusable UI controls — code components — using TypeScript, HTML, and CSS. A code component can replace the default renderer for a single field or an entire dataset (a view or subgrid), and it can run in model-driven apps and, when enabled, canvas apps as well. This section covers how a component is scaffolded and declared, and the lifecycle methods that drive it at runtime.
Scaffolding a Component with the Power Platform CLI
Development starts with the Power Platform CLI (pac):
pac pcf init --namespace Contoso --name RatingControl --template field
The --template flag chooses between two fundamentally different component shapes:
- field — binds to a single table column and renders/edits one value at a time, replacing the default control for that data type.
- dataset — binds to a list of records (a view or subgrid) instead of one field, giving the component access to multiple rows and columns at once.
This scaffold produces ControlManifest.Input.xml, an index.ts entry point, and standard package.json/tsconfig.json project files.
The Control Manifest
ControlManifest.Input.xml is the declarative contract between the component and the platform. Its key elements:
| Element | Declares |
|---|---|
<control> | namespace, constructor (the exported class name), version, display-name-key, description-key |
<property> | a bindable input/output/bound value — for a field component, exactly one usage="bound" property typically defines the data type the control attaches to |
<data-set> | for dataset components only — the columns/targets the component binds to when attached to a view or subgrid, replacing a single <property> |
<resources> | the compiled code bundle, CSS files, and RESX resource strings for localization; can reference shared platform-library resources (e.g., React) instead of bundling them |
<feature-usage> | platform capabilities the component needs at runtime — WebAPI, Utility, or specific Device features — which must be declared here before the corresponding context API is usable |
Omitting a <feature-usage> declaration for a capability the code actually calls at runtime — such as context.webAPI — is a common source of components that behave correctly in the local test harness but fail once packaged, because the manifest is what the platform inspects before granting that access.
Lifecycle Methods
index.ts implements a class satisfying ComponentFramework.StandardControl<IInputs, IOutputs> (or a React-based variant covered in the next section). The platform calls four methods, in this order over the component's life:
init(context, notifyOutputChanged, state, container)— called exactly once when the component first loads. Receives the initialcontext, stores thenotifyOutputChangedcallback for later use, restores any previously persistedstate, and — for non-React controls — builds the initial DOM insidecontainer.updateView(context)— called every time bound data changes elsewhere (for example, the field's value is updated by a different control or a business rule) or the container is resized. This is where the component re-renders based on the latestcontext.parametersvalues; it can be called many times over the component's life.getOutputs()— called by the platform to pull the component's current output value(s) after the component has callednotifyOutputChanged()to signal a change; the return shape must match the manifest's declaredIOutputs.destroy()— called once when the component is being removed from the DOM, such as when the user navigates away from the form. This is where event listeners, timers, or other resources acquired ininit()should be released.
Field vs. Dataset in Practice
A field-type component's manifest binds one <property> to a specific table column data type — for example, a whole number — and context.parameters in init/updateView exposes that single typed value along with formatting and security metadata. A dataset-type component's manifest instead declares a <data-set>, and context.parameters.<datasetName> exposes the bound records, columns, sorted record IDs, and paging as a collection rather than a single value. Choosing the wrong template at pac pcf init time — field when the requirement is really "customize how this subgrid renders" — means rebuilding the manifest and much of the component's data-access code from scratch, which is why PL-400 scenario questions frequently test whether you can identify field versus dataset from the requirement alone before any code is written.
Flexible Typing and Localization
A <property> can bind to a fixed data type (for example, SingleLine.Text) or to a <type-group>, which lists several compatible types (such as whole number, currency, and decimal) so the same component can be dropped onto any column matching one of those types without a separate manifest per data type. This flexible-typing option is what lets one rating-control component, for instance, be reused across several numeric columns of different subtypes rather than forcing a developer to fork the project for each one.
Localization is handled through the <resources> element's RESX files rather than hardcoded strings in TypeScript: display-name-key and description-key on the control and its properties point at resource keys, and a RESX file per supported language culture supplies the localized text, so the same compiled bundle renders correctly across languages configured in the environment without a code change.
Validating the Manifest
pac pcf build (or the build step inside npm start/npm run build) compiles index.ts into bundle.js and validates the manifest against the platform's schema, catching mismatches such as a <property> referencing an of-type the framework doesn't recognize, or a missing required attribute, before the component is ever pushed or packaged. Treating this build step as a checkpoint — not just packaging noise — is worth remembering, since manifest errors caught here are far cheaper to fix than the same errors discovered after a solution has already been imported into a downstream environment.
In ControlManifest.Input.xml for a field-type PCF component, which element must declare that the component will call context.webAPI at runtime?
Which PCF lifecycle method is called every time the bound field's value changes elsewhere on the form, or the component's container is resized?