PCF – How to use images in component

A few days ago a twitter user (kr!$#n@) mentioned me and some other developers in a tweet. Included was a question regarding PCF.

I was happy to help and honored to be one of the persons the community asks for help. I suggested to use the Office UI Fabric. Thanks to Scott for mentioning that it is now called differently – Fluent UI. I really appreciate it. I always try to get the names right (even if they change a lot).

But the goal of kr!$#n@ is to not include any external urls.

In my opinion, this can only be achieved when you ship the icons as images and use them inside of the PCF component. I provided a demo repo on GitHub for kr!$#n@.

Since this was a question in the community I thought it is worth an article.

In this article we will learn how to ship and use images in a PCF component.

The idea

Actually I have never had the need for images in one of my PCF components so far. But since there is the image type for resources (link to docu) I thought: “Can’t be that hard to implement”.

Unfortunately, it is not as straight forward as I thought/hoped it would be. I searched for a solution and found a discussion in the Power Apps community. The fifth answer (as well from Andrew Ly, who also has asked the question) gave me the hint/solution. It was not 100% working for me but it was pretty close.

The idea is to load the image as a resource, defined in the manifest, via the PCF API (link to docu). The response can then be used as a data URL in the source of an image tag.

Lets dive in and see how this could be achieved.

Update (2021-04-09): SVG-Images are currently not supported. The content you get from the WebAPI when retrieving the image will be empty when you are using SVG-Images.

Structure

Lets take a look at our file and folder structure.

First of all, we have to create a new PCF component project. I used the following command in VS Code.

pac pcf init --namespace Demo --name IconDemoComponent --template field

This will create a new project “IconDemoComponent” with the namespace “Demo”.

After creating a new PCF component project we end with the following structure.

|- IconDemoComponent
|- generated
|- ManifestTypes.d.ts
|- ControlManifest.input.xml
|- index.ts
|- package-lock.json
|- package.json
|- ...

Since this will be a pretty simple implementation we will only add an image and a css file. Both in a separate folder (one css and one img).

For demo purpose I have created a simple PNG-File from the free FontAwesome user icon (link).

PNG-File

The following should be our new structure

|- IconDemoComponent
|- css
|- IconDemoComponent.css
|- generated
|- ManifestTypes.d.ts
|- img
|- user.png
|- ControlManifest.input.xml
|- index.ts
|- package-lock.json
|- package.json
|- ...

Manifest

In the Manifest we only have to add two resources.

  • The CSS -File and
  • The PNG-File

Both will be added within the resources tag.

The CSS-File with the following line

<css path="css/IconDemoComponent.css" order="1" />

The PNG-File with the following line

<img path="img/user.png" />

The Manifests resource tag should look like this:

Resource-Tag in Manifest
Resource-tag in Manifest

CSS

The css will only define the height and width of our img-tag.

img{
width:100px;
height:100px;
}

TypeScript

The index.ts is the most interesting file in this implementation.

First of all we have to define the img element.

private imgElement:HTMLImageElement;

In the “init” function we will define the img element, request the getResource endpoint and append the img element to the container. This will be achieved with the following 3 lines

this.imgElement = document.createElement("img");
context.resources.getResource("user.png", this.setImage.bind(this, false, "png"), this.showError.bind(this));
container.appendChild(this.imgElement);

The important part is the second line. The getResource (docu) function expects three parameters

  • Image id
  • success callback
  • failure callback

Since the PowerApps Component Framework will flatten the file structure we have to “search” the image in the root folder and not in the “img” folder, where we actually have saved it. This is the reason why we only use “user.png” and not “img/user.png”.

Our showError function does actually nothing in this demo. It is just present because the getResource function does need it.

private showError(): void
{
}

The setImage function is already a bit more interesting.

private setImage(shouldUpdateOutput:boolean, fileType: string, fileContent: string): void
{
let imageUrl:string = this.generateImageSrcUrl(fileType, fileContent);
this.imgElement.src = imageUrl;
}

In this function, we do get the image data Url from a function called “generateImageSrcUrl”. This function takes the file type and file content (in base64). We will look at this function in a minute. We then set the image data Url as the source of the img element.

The most important part is the “generateImageSrcUrl” function, even though it basically only is one line.

private generateImageSrcUrl(fileType: string, fileContent: string): string
{
return  "data:image/" + fileType + ";base64, " + fileContent;
}

The data URL basically begins with “data” followed by the file type, in our case “image/png”. The file type comes partly as a parameter. After that, we have the indicator that the content is presented in base64. At the end, we do have the actual file content.

Conclusion

If we know how it is working it basically are only some rows of code to achieve our goal of using images in a PCF component.

One important thing to mention is, that this will not work in the harness. It only works in an actual Model-Driven App (MDA). This was mentioned by Oleksandr (OOlashyn) in the mentioned Community discussion.

You can find the demo Repo on GitHub.

https://github.com/BenediktBergmann/PCF-Image-Demo

I hope you enjoyed this article. Please leave me a comment or contact me in another way if you have any questions or feedback.

This is just 1 of 61 articles. You can browse through all of them by going to the main page. Another possibility is to view the categories page to find more related content.
You can also subscribe and get new blog posts emailed to you directly.
Enter your email address to receive notifications of new posts by email.

Loading
11 Comments
  1. Avatar
    • Avatar
  2. Avatar
  3. Avatar
  4. Avatar
  5. Avatar
  6. Avatar

Add a Comment

Your email address will not be published. Required fields are marked *