Build your first Field Customizer extension
Extensions are client-side components that run inside the context of a SharePoint page. Extensions can be deployed to SharePoint Online, and you can use modern JavaScript tools and libraries to build them.
You can follow these steps by watching the video on the Microsoft 365 Platform Communtiy (PnP) YouTube Channel:
Create an extension project
Create a new project directory in your favorite location.
md field-extension
Go to the project directory.
cd field-extension
Create a new HelloWorld extension by running the Yeoman SharePoint Generator.
yo @microsoft/sharepoint
When prompted, enter the following values (select the default option for all prompts omitted below):
- What is your solution name?: field-extension
- Which type of client-side component to create?: Extension
- Which type of client-side extension to create? Field Customizer
- What is your Field Customizer name? HelloWorld
- Which template would you like to use?: No JavaScript Framework
At this point, Yeoman installs the required dependencies and scaffolds the solution files along with the HelloWorld extension. This might take a few minutes.
Type the following into the console to start Visual Studio Code.
code .
Note
Because the SharePoint client-side solution is HTML/TypeScript based, you can use any code editor that supports client-side development to build your extension.
Open the ./src/extensions/helloWorld/HelloWorldFieldCustomizer.manifest.json file.
This file defines your extension type and a unique identifier
id
for your extension. You need this unique identifier later when debugging and deploying your extension to SharePoint.
Code your Field Customizer
Open the ./src/extensions/helloWorld/HelloWorldFieldCustomizer.ts file.
Notice that the base class for the Field Customizer is imported from the sp-listview-extensibility package, which contains SharePoint Framework code required by the Field Customizer.
import { Log } from '@microsoft/sp-core-library';
import { override } from '@microsoft/decorators';
import {
BaseFieldCustomizer,
IFieldCustomizerCellEventParameters
} from '@microsoft/sp-listview-extensibility';
The logic for your Field Customizer is contained in the onInit()
, onRenderCell()
, and onDisposeCell()
methods.
onInit()
is where you'll execute the setup needed for your extension. This event occurs afterthis.context
andthis.properties
are assigned, but before the page DOM is ready. As with web parts,onInit()
returns a promise that you can use to do asynchronous operations;onRenderCell()
isn't called until your promise has resolved. If you don’t need that, simply returnPromise.resolve<void>();
.onRenderCell()
occurs when each cell is rendered. It provides anevent.domElement
HTML element where your code can write its content.onDisposeCell()
occurs immediately before theevent.cellDiv
is deleted. It can be used to free any resources that were allocated during field rendering. For example, ifonRenderCell()
mounted a React element,onDisposeCell()
must be used to free it; otherwise, a resource leak would occur.
The following are the contents of onRenderCell()
and onDisposeCell()
in the default solution:
@override
public onRenderCell(event: IFieldCustomizerCellEventParameters): void {
// Use this method to perform your custom cell rendering.
const text: string = `${this.properties.sampleText}: ${event.fieldValue}`;
event.domElement.innerText = text;
event.domElement.classList.add(styles.cell);
}
@override
public onDisposeCell(event: IFieldCustomizerCellEventParameters): void {
super.onDisposeCell(event);
}
Debug your Field Customizer
You can't use the local Workbench to test SharePoint Framework Extensions. You need to test and develop them directly against a live SharePoint Online site. You don't have to deploy your customization to the app catalog to do this, which makes the debugging experience simple and efficient.
To test your extension, you'll need to first create the field to test the customizer in. So move to the site in your SharePoint Online tenant where you want to test the field customizer.
Navigate to the Site Contents page.
On the toolbar, select New, and then select List.
Create a new list named Orders, and then select Create.
Select the + sign, and then select Number to create a new Number field for the list.
Set the name of the field to Percent, and then select Save.
Add a few items with different numbers in the percent field. We'll modify the rendering later in this tutorial, so the different numbers will be presented differently based on your custom implementation.
Within Visual Studio Code, open the ./config/serve.json file.
Set the
InternalFieldName
attribute toPercent
for the field name, which we created. Update thepageUrl
attributes to match a URL of the list which we created in the preview steps. After the changes, your serve.json should look like the following code:{ "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/spfx-serve.schema.json", "port": 4321, "https": true, "serveConfigurations": { "default": { "pageUrl": "https://sppnp.sharepoint.com/sites/Group/Lists/Orders/AllItems.aspx", "fieldCustomizers": { "Percent": { "id": "b909e395-f714-421f-94e0-d638dafeb210", "properties": { "sampleText": "Value" } } } }, "helloWorld": { "pageUrl": "https://sppnp.sharepoint.com/sites/Group/Lists/Orders/AllItems.aspx", "fieldCustomizers": { "Percent": { "id": "b909e395-f714-421f-94e0-d638dafeb210", "properties": { "sampleText": "Value" } } } } } }
Note
The GUID in the above JSON excerpt is the unique ID of the SPFx extension component. This is defined in the component's manifest. The GUID in your solution will be different as every component ID is unique.
Compile your code and host the compiled files from the local machine by running this command:
gulp serve
When the code compiles without errors, it serves the resulting manifest from https://localhost:4321.
This will start your default browser and load the page defined in serve.json file.
Accept the loading of debug manifests by selecting Load debug scripts when prompted.
Notice how the Percent column values are now presented with an additional prefix string as Value:, which is provided as a property for the Field Customizer.
Enhance the Field Customizer rendering
Now that we've successfully tested the out-of-the-box starting point of the Field Customizer, let's modify the logic slightly to have a more polished rendering of the field value.
Open the ./src/extensions/helloWorld/HelloWorldFieldCustomizer.module.scss file, and update the styling definition as follows.
.HelloWorld { .cell { display: inline-block; } .full { background-color: #e5e5e5; width: 100px; } }
Open the file ./src/extensions/helloWorld/HelloWorldFieldCustomizer.ts and update the
onRenderCell()
method as follows.@override public onRenderCell(event: IFieldCustomizerCellEventParameters): void { event.domElement.classList.add(styles.cell); event.domElement.innerHTML = ` <div class='${styles.HelloWorld}'> <div class='${styles.full}'> <div style='width: ${event.fieldValue}px; background:#0094ff; color:#c0c0c0'> ${event.fieldValue} </div> </div> </div>`; }
In the console window, ensure that you don't have any errors. If the solution isn't running, execute the following task:
gulp serve
In your previously created list, refresh the browser window with the debugging query parameters or restart the browser with gulp serve.
Accept the loading of debug manifests by selecting Load debug scripts when prompted.
Note how we changed the field rendering style completely. The field value is indicated by using a graphical representation of the value.
Add the field definition to the solution package for deployment
Now that we've tested our solution properly in debug mode, we can package this to be deployed automatically as part of the solution package deployed to the sites.
Install the solution package to the site where it should be installed, so that the extension manifest is included for execution.
Associate the Field Customizer to an existing field in the site. You can do this programmatically using the SharePoint REST or CSOM API, or by using the Feature Framework in of the SharePoint Framework solution package. In this tutorial, we'll use the Feature Framework's xml files. You need to associate the following properties in the
SPField
object at the site or list level.ClientSideComponentId
: this is the identifier (GUID) of the Field Customizer that has been installed in the app catalog.ClientSideComponentProperties
: this is an optional parameter that can be used to provide properties for the Field Customizer instance.
You can control the requirement to add a solution containing your extension to the site with the
skipFeatureDeployment
property in ./config/package-solution.json file. Even though you wouldn't require the solution to be installed on the site, you'd need to associateClientSideComponentId
to specific objects for the extension to be visible.You can use, for example, Set-PnPField cmdlet from PnP PowerShell cmdlets to programatically associate an extension to existing fields in your sites.
Note
PnP PowerShell is an open-source solution with active community providing support for it. There is no SLA for the open-source tool support from Microsoft.
Review the elements.xml file
In the following steps, we review the default field definition, which was automatically created and will then be used to automatically deploy needed configurations when the solution package is installed on a site.
Return to your solution in Visual Studio Code (or to your preferred editor).
Open the file ./sharepoint/assets/elements.xml.
Look at the XML in this file. The ClientSideComponentId
property has been automatically updated to the unique ID of your Field Customizer available in the ./src/extensions/helloWorld/HelloWorldFieldCustomizer.manifest.json file. You'll need to adjust this file matching on your field type and details.
Note
For more information on the Feature Framework XML schema, see: SharePoint schema reference.
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Field ID="{060E50AC-E9C1-3D3C-B1F9-DE0BCAC200F6}"
Name="SPFxPercentage"
DisplayName="Percentage"
Type="Number"
Min="0"
Required="FALSE"
Group="SPFx Columns"
ClientSideComponentId="7e7a4262-d02b-49bf-bfcb-e6ef1716aaef">
</Field>
</Elements>
Ensure that definitions are taken into account within the build pipeline
Open the ./config/package-solution.json file.
The package-solution.json file defines the package metadata as shown in the following code. To ensure that the elements.xml file is taken into account while the solution package is created, the default scaffolding of this file is updated to include additional details for a feature definition. This feature definition is used to provision and execute the elements.xml file.
Also notice that the includeClientSideAssets
attribute is set to true
. This means the JavaScript assets will be included in the *.sppkg file:
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "field-extension-client-side-solution",
"id": "80c04d1b-dca7-4d0a-86c0-9aedf904704f",
"version": "1.0.0.0",
"includeClientSideAssets": true,
"isDomainIsolated": false,
"features": [
{
"title": "Application Extension - Deployment of custom action.",
"description": "Deploys a custom action with ClientSideComponentId association",
"id": "b27507b9-7c2a-4398-8946-7438571f16f6",
"version": "1.0.0.0",
"assets": {
"elementManifests": [
"elements.xml"
]
}
}
]
},
"paths": {
"zippedPackage": "solution/field-extension.sppkg"
}
}
Deploy the field to SharePoint Online and host JavaScript from local host
Now you're ready to deploy the solution to a SharePoint site and get the field association automatically included in a field. We'll use the --ship option with this packaging so all assets are packaged automatically in the solution package.
In the console window, enter the following command to package your client-side solution that contains the extension so that we get the basic structure ready for packaging:
gulp bundle --ship
Execute the following command so that the solution package is created:
gulp package-solution --ship
The command creates the package: ./sharepoint/solution/field-extension.sppkg:
You now need to deploy the package that was generated to the app catalog. To do this, go to your tenant's app catalog and open the Apps for SharePoint library.
Upload the file ./sharepoint/solution/field-extension.sppkg to the app catalog.
Notice that SharePoint displays the trust dialog and asks you to trust the client-side solution with SharePoint Online as the domain. Your assets will be automatically hosted either from app catalog URL or from Microsoft 365 public CDN, depending on your tenant settings.
Select the Deploy button.
Go to the site where you want to test SharePoint asset provisioning. This could be any site collection in the tenant where you deployed this solution package.
Select the gears icon on the top navigation bar on the right, and then select Add an app.
In the Search box, enter field, and then select ENTER to filter your apps.
Select the field-extension-client-side-solution app to install the solution on the site. After the installation is complete, refresh the page.
When the solution has been installed, select New from the toolbar on the Site Contents page, and then select List.
Create a list named Invoices.
When the new list is created, on the Site Contents page, select Settings from the menu of the newly created list.
Select Columns > Add from existing site columns.
Select the SPFx Columns > Percentage field that was provisioned from the solution package, and then select OK.
Go to the newly created Invoices list. Add a few items to the list with different values in the Percentage column to determine how the field is rendering without the debug query parameters.
The process for publishing your app is identical among the different extension types.
Note
This was a relatively simple field customizer with functionality that could also have been achieved with Use column formatting to customize SharePoint. Column formatting however does not support actual custom code. Notice that field customizers cannot be modified by end users from the user interface which enables additional use cases.