Introduction to creating a code component
Power Apps provides a ton of functionality out of the box for app makers to build their apps, but sometimes there's a need to build a piece of UI that isn't included. Examples might be replacing a percentage value with a gauge, rendering a bar code rather than an ID, or replacing existing controls with controls that have more functionality—like a drag-and-drop grid view. You can also wrap the Power Apps component framework around existing components you've written in other web frameworks, like React or Angular.
Creating these components allows you to use the full breadth of the modern web development ecosystem: the libraries, frameworks, and other tools you may be familiar with, and packages that capability into a form that allows app makers to build apps with your code, as if it were an out of the box part of the platform.
Note
You can find other sample components including those that use Angular or React in our documentation.
Custom Power Apps components are frequently referred to as code components because they require custom code to implement them. They consist of three elements: a manifest, an implementation, and resources. In the following exercise, you'll write your own custom code component: a hello world message that will look like the following image.
This component will listen to changes coming from the host app, and allow the user to make changes that are then pushed to the host app. The following steps will help you build this component.
Install Power Platform CLI
To prepare your computer to build code components, follow these steps:
Install Npm (comes with Node.js) or Node.js (comes with npm). We recommend that you use a LTS (Long-Term Support) version.
Install Visual Studio Code.
Install Power Platform Tools extension.
Create a new component project
To create a new component project, follow these steps:
Create a directory where you'll build your component. In this sample, you'll place the component in C:\source\hello-pcf; however, you can create your own directory. To create your own directory, you'll use a Visual Studio Code.
Start Visual Studio Code.
Select Terminal and select New Terminal.
Change directory to your source folder.
cd source
From your source directory, create a directory named hello-pcf.
md hello-pcf
Change directory to hello-pcf.
cd hello-pcf
Initialize your component project by using Power Platform CLI with the following command:
pac pcf init --namespace SampleNamespace --name HelloPCF --template field
The following image shows an example of the output that you should see.
Install the project build tools by using the command
npm install
. You might see some warnings displayed; however, you can ignore them.npm install
Run the command below to open project in Visual Studio Code.
code -a .
The project contents should look like the image below.
Update your code component's manifest
Update the manifest file to accurately represent your control.
Expand the HelloPCF folder and open the ControlManifest.Input.xml file.
Change the version to 1.0.0 and description-key to Says hello.
Locate the <property node.
Change the name value to Name, display-name-key to Name, and description-key to A name.
Locate the <resources> node.
Include a reference to a CSS file named hello-pcf.css that you'll create.
<css path="css/hello-pcf.css" order="1" />
Save your changes by selecting File and then Save.
Add styling to your code component
To add styling to your code component, follow these steps:
Make sure you still have the ControlManifest.Input.xml file selected and then select New Folder.
Name the new folder css.
Select the css folder you created and select New File.
Name the new file hello-pcf.css.
Open the new hello-pcf.css file you created, and paste the following CSS snippet.
.SampleNamespace\.HelloPCF { font-size: 1.5em; }
The content of the CSS file should now look like the image below.
Select File and select Save.
Build your code component
Before you can implement your component logic, you need to run a build on your component. This makes sure that the right TypeScript types are generated to match the properties in your ControlManifest.xml document.
Go back to the terminal and build your project by using the following command.
npm run build
The component is compiled into the out/controls/HelloPCF directory. The build artifacts include:
css folder
bundle.js - Bundled component source code
ControlManifest.xml - Actual component manifest file that is uploaded to the Microsoft Dataverse organization
Implement your code component's logic
To implement your code component's logic, follow these steps:
Open index.ts file.
Above the constructor method, insert the following private variables:
// The PCF context object\ private context: ComponentFramework.Context<IInputs>; // The wrapper div element for the component\ private container: HTMLDivElement; // The callback function to call whenever your code has made a change to a bound or output property\ private notifyOutputChanged: () => void; // Flag to track if the component is in edit mode or not\ private isEditMode: boolean; // Tracking variable for the name property\ private name: string | null;
Locate the init method and replace it with the method below.
public init(context: ComponentFramework.Context<IInputs>, notifyOutputChanged: () => void, state: ComponentFramework.Dictionary, container: HTMLDivElement) { // Track all the things this.context = context; this.notifyOutputChanged = notifyOutputChanged; this.container = container; this.isEditMode = false; }
Add the snippet below to the init method. This snippet will create the span that will hold the hello message.
// Create the span element to hold the hello message const message = document.createElement("span"); message.innerText = `Hello ${this.isEditMode ? "" :context.parameters.Name.raw}`;
Add the snippet below to the init method. This code will create a textbox to edit the name.
// Create the textbox to edit the name const textbox = document.createElement("input"); textbox.type = "text"; textbox.style.display = this.isEditMode ? "block" : "none";
Add the below if statement to init method.
if (context.parameters.Name.raw) { }
Add the snippet below inside the if statement. This snippet will set the textbox value and wrap the textbox and the message in a div.
textbox.value = context.parameters.Name.raw; // Wrap the two above elements in a div to box out the content const messageContainer = document.createElement("div"); messageContainer.appendChild(message); messageContainer.appendChild(textbox);
Add the snippet below inside the if statement. This snippet will create a button that will switch between edit and read mode.
// Create the button element to switch between edit and read modes const button = document.createElement("button"); button.textContent = this.isEditMode ? "Save" : "Edit"; button.addEventListener("click", () => { this.buttonClick(); });
Add the snippet below inside the if statement. This snippet will add the message container and a button to the main container.
// Add the message container and button to the overall control container this.container.appendChild(messageContainer); this.container.appendChild(button);
The init method should now look like the image below.
Add button select handler method. Add the following method below the init method.
public buttonClick() { }
Add the snippet below inside the buttonClick method. This snippet will get the controls via DOM queries.
// Get our controls via DOM queries const textbox = this.container.querySelector("input")!; const message = this.container.querySelector("span")!; const button = this.container.querySelector("button")!;
Add the snippet below inside the buttonClick method. This snippet will copy the text value to name and the call the notify method if in edit mode.
// If not in edit mode, copy the current name value to the textbox if (!this.isEditMode) { textbox.value = this.name ?? ""; } else if (textbox.value != this.name) { // if in edit mode, copy the textbox value to name and call the notify callback this.name = textbox.value; this.notifyOutputChanged(); }
Add the snippet below inside the buttonClick method. This snippet will flip the mode flag.
// flip the mode flag this.isEditMode = !this.isEditMode;
Add the snippet below inside the buttonClick method. This snippet will set up new output, based on the changes.
// Set up the new output based on changes message.innerText = `Hello ${this.isEditMode ? "" : this.name}`; textbox.style.display = this.isEditMode ? "inline" : "none"; textbox.value = this.name ?? ""; button.textContent = this.isEditMode ? "Save" : "Edit";
The buttonClick method should now look like the image below.
Locate the updateView method and replace it with the method below.
public updateView(context: ComponentFramework.Context<IInputs>): void { // Checks for updates coming in from outside this.name = context.parameters.Name.raw; const message = this.container.querySelector("span")!; message.innerText = `Hello ${this.name}`; }
Locate the replace getOutputs and replace it with the method below.
public getOutputs(): IOutputs { return { // If our name variable is null, return undefined instead Name: this.name ?? undefined }; }
Locate the destroy method and replace it with the method below.
public destroy() { // Remove the event listener we created in init this.container.querySelector("button")!.removeEventListener("click", this.buttonClick); }
The updateView, getOutputs, and destroy methods should now look like the image below.
Rebuild and run your code component
To rebuild and run your code component, follow these steps:
Now that your component's logic is implemented, go back to the terminal and rebuild it by using the command below.
npm run build
The build should succeed.
Run your component in Node's test harness by running the command below.
npm start
Note
You can also enable watch mode to ensure that any changes to the following assets are made automatically without having to restart the test harness by using the
npm start watch
command.index.ts file.
ControlManifest.Input.xml file.
Imported libraries in index.ts.
All resources listed in the manifest file
New browser window should load the test harness. (The window should open automatically, but you can also reference the address as found in the command window).
Select Edit.
Enter World and select Save.
You may change the container size.
The test harness should now look like the image below.
Close the test harness browser window.
Go back to the terminal and stop the watcher by holding [CONTROL] + C.
Type Y and then [ENTER].
Need help? See our troubleshooting guide or provide specific feedback by reporting an issue.