Using the PnP PropertyFieldButton Control in an SPFx Web Part

The PropertyFieldButton control adds an action button directly to the SharePoint Framework Property Pane.

Unlike input controls such as text fields, dropdowns, or checkboxes, this control does not necessarily store a value in the Web Part properties. Instead, it executes an action when the user clicks the button.

Some possible uses include:

  • Opening a configuration page
  • Resetting Web Part settings
  • Testing a connection
  • Starting a custom operation
  • Displaying a confirmation message
  • Loading or refreshing configuration data

This article demonstrates a basic implementation in which the button displays a browser alert.

The example is part of our broader PnP controls learning roadmap, where each control is tested in its own SPFx Web Part.

Official documentation

The official documentation for this control is available here:

PropertyFieldButton – PnP SPFx Property Controls

The PnP SPFx Property Controls project provides reusable controls designed specifically for the SPFx Property Pane.


Install the PnP Property Controls package

Open PowerShell in the root directory of the existing SPFx solution.

npm install @pnp/spfx-property-controls --save

After installing the dependency, restore the remaining project packages:

npm install

Import PropertyFieldButton

Open the main Web Part file and add the following import:

import {
PropertyFieldButton
} from '@pnp/spfx-property-controls/lib/PropertyFieldButton';

The import comes from the specific control directory instead of importing the entire library.


Web Part properties

For this example, the Web Part only stores the standard description property:

export interface IPropertyFieldButtonWpWebPartProps {
description: string;
}

A property such as Btn is not required because the button executes an action rather than storing a selected value.

If a string property were necessary, the TypeScript type should be written as lowercase string:

buttonValue: string;

Avoid using the JavaScript wrapper type:

buttonValue: String;

For the current example, neither property is needed.


Complete Web Part file

Open:

src/webparts/propertyFieldButtonWp/PropertyFieldButtonWpWebPart.ts

Replace its content with the following code:

import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
type IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-property-pane';
import {
BaseClientSideWebPart
} from '@microsoft/sp-webpart-base';
import {
IReadonlyTheme
} from '@microsoft/sp-component-base';
import * as strings from 'PropertyFieldButtonWpWebPartStrings';
import PropertyFieldButtonWp
from './components/PropertyFieldButtonWp';
import {
IPropertyFieldButtonWpProps
} from './components/IPropertyFieldButtonWpProps';
import {
PropertyFieldButton
} from '@pnp/spfx-property-controls/lib/PropertyFieldButton';
export interface IPropertyFieldButtonWpWebPartProps {
description: string;
}
export default class PropertyFieldButtonWpWebPart
extends BaseClientSideWebPart<IPropertyFieldButtonWpWebPartProps> {
private _isDarkTheme: boolean = false;
private _environmentMessage: string = '';
public render(): void {
const element: React.ReactElement<IPropertyFieldButtonWpProps> =
React.createElement(
PropertyFieldButtonWp,
{
description: this.properties.description
}
);
ReactDom.render(element, this.domElement);
}
protected onInit(): Promise<void> {
return this._getEnvironmentMessage().then(
(message: string): void => {
this._environmentMessage = message;
}
);
}
private _getEnvironmentMessage(): Promise<string> {
if (this.context.sdks.microsoftTeams) {
return this.context.sdks.microsoftTeams.teamsJs.app
.getContext()
.then((context): string => {
let environmentMessage: string = '';
switch (context.app.host.name) {
case 'Office':
environmentMessage =
this.context.isServedFromLocalhost
? strings.AppLocalEnvironmentOffice
: strings.AppOfficeEnvironment;
break;
case 'Outlook':
environmentMessage =
this.context.isServedFromLocalhost
? strings.AppLocalEnvironmentOutlook
: strings.AppOutlookEnvironment;
break;
case 'Teams':
case 'TeamsModern':
environmentMessage =
this.context.isServedFromLocalhost
? strings.AppLocalEnvironmentTeams
: strings.AppTeamsTabEnvironment;
break;
default:
environmentMessage = strings.UnknownEnvironment;
}
return environmentMessage;
});
}
return Promise.resolve(
this.context.isServedFromLocalhost
? strings.AppLocalEnvironmentSharePoint
: strings.AppSharePointEnvironment
);
}
protected onThemeChanged(
currentTheme: IReadonlyTheme | undefined
): void {
if (!currentTheme) {
return;
}
this._isDarkTheme = !!currentTheme.isInverted;
const { semanticColors } = currentTheme;
if (semanticColors) {
this.domElement.style.setProperty(
'--bodyText',
semanticColors.bodyText || null
);
this.domElement.style.setProperty(
'--link',
semanticColors.link || null
);
this.domElement.style.setProperty(
'--linkHovered',
semanticColors.linkHovered || null
);
}
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration():
IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('description', {
label: strings.DescriptionFieldLabel
}),
PropertyFieldButton('', {
key: 'buttonID',
text: 'Button',
disabled: false,
isPrimary: true,
isVisible: true,
onClick: (): void => {
alert('Button Clicked');
}
})
]
}
]
}
]
};
}
}

Component properties interface

Open:

src/webparts/propertyFieldButtonWp/components/IPropertyFieldButtonWpProps.ts

Use the following interface:

export interface IPropertyFieldButtonWpProps {
description: string;
}

The component only receives the description because the button exists inside the Property Pane, not inside the React component.


React component

Open:

src/webparts/propertyFieldButtonWp/components/PropertyFieldButtonWp.tsx

Use the following code:

import * as React from 'react';
import {
IPropertyFieldButtonWpProps
} from './IPropertyFieldButtonWpProps';
const PropertyFieldButtonWp:
React.FC<IPropertyFieldButtonWpProps> = ({
description
}) => {
return (
<section>
<h2>PropertyFieldButton Demo</h2>
<p>{description}</p>
<p>
Open the Web Part Property Pane and click the configured button.
</p>
</section>
);
};
export default PropertyFieldButtonWp;

The button is not rendered by this React component. It is rendered automatically by SPFx inside the Web Part Property Pane.


Adding the control to the Property Pane group

Every Property Pane page contains one or more groups:

groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
// Property Pane controls
]
}
]

The PropertyFieldButton must be added to the groupFields array:

groupFields: [
PropertyPaneTextField('description', {
label: strings.DescriptionFieldLabel
}),
PropertyFieldButton('', {
key: 'buttonID',
text: 'Button',
disabled: false,
isPrimary: true,
isVisible: true,
onClick: (): void => {
alert('Button Clicked');
}
})
]

The first parameter is normally the target property name for a Property Pane control:

PropertyFieldButton('', {

In this case, an empty string is used because the button does not need to save a value in this.properties.


Understanding the properties

key

key: 'buttonID'

The key uniquely identifies the control inside the Property Pane.

Each control should have its own key.


text

text: 'Button'

Defines the text displayed inside the button.

A more descriptive value could be used:

text: 'Test configuration'

disabled

disabled: false

Controls whether the user can click the button.

To disable it:

disabled: true

This property can also depend on another Web Part property:

disabled: !this.properties.description

In this example, the button will only be enabled when the description contains a value.


isPrimary

isPrimary: true

Defines whether the button should use the primary visual style.

Primary button:

isPrimary: true

Standard button:

isPrimary: false

isVisible

isVisible: true

Controls whether the button appears in the Property Pane.

The property name is case-sensitive.

Use:

isVisible: true

Do not use:

isVIsible: true

The uppercase I in the middle produces an invalid property name.


onClick

onClick: (): void => {
alert('Button Clicked');
}

The onClick callback defines the operation executed when the user clicks the button.

For this demonstration, it displays a browser alert.

In a real solution, the callback could call a private Web Part method:

onClick: this._handleButtonClick

The method could then be defined inside the Web Part class:

private _handleButtonClick = (): void => {
alert('Button Clicked');
};

Using a separate method is useful when the action contains more than a few lines of code.


Object syntax versus JSX syntax

A common mistake is using JSX property syntax inside the Property Pane configuration.

This syntax is invalid inside a TypeScript object:

PropertyFieldButton('', {
text={"Button"},
key={"buttonID"},
disabled={false}
})

The {} syntax is normally used when passing values to React components in JSX:

<MyComponent
text={"Button"}
disabled={false}
/>

However, PropertyFieldButton receives a regular TypeScript configuration object.

The correct syntax uses colons:

PropertyFieldButton('', {
text: 'Button',
key: 'buttonID',
disabled: false
})

Optional styling properties

The control also supports optional configuration such as:

className
styles
iconProps

These properties should only be added when their values have been declared.

For example, an icon can be configured directly:

PropertyFieldButton('', {
key: 'buttonID',
text: 'Run action',
disabled: false,
isPrimary: true,
isVisible: true,
iconProps: {
iconName: 'Play'
},
onClick: (): void => {
alert('Action started');
}
})

Do not add undeclared variables such as:

className: className,
styles: styles,
iconProps: iconProps

TypeScript will report that the names cannot be found unless those variables have previously been defined.


Refreshing the Property Pane

When the button changes Web Part properties programmatically, it may be necessary to refresh the Property Pane and render the Web Part again.

For example:

private _resetProperties = (): void => {
this.properties.description = '';
this.context.propertyPane.refresh();
this.render();
};

The button can then call the method:

PropertyFieldButton('', {
key: 'resetButton',
text: 'Reset properties',
disabled: false,
isPrimary: false,
isVisible: true,
onClick: this._resetProperties
})

This pattern is useful for reset buttons and configuration utilities.


Start the SPFx project

Run the project with Heft:

heft start

Open the SharePoint-hosted Workbench and add the Web Part.

Edit the Web Part to open its Property Pane.

The Property Pane will display:

  1. The standard description text field
  2. The PnP PropertyFieldButton
  3. The configured action when the button is clicked

Conclusion

PropertyFieldButton is a simple but useful PnP SPFx Property Pane control.

Its main difference from most Property Pane controls is that it represents an action rather than a stored value. For this reason, the control can use an empty property name and execute its logic through the onClick callback.

The most important implementation details are:

import {
PropertyFieldButton
} from '@pnp/spfx-property-controls/lib/PropertyFieldButton';
PropertyFieldButton('', {
key: 'buttonID',
text: 'Button',
disabled: false,
isPrimary: true,
isVisible: true,
onClick: (): void => {
alert('Button Clicked');
}
})

Remember that the configuration is a TypeScript object, so its properties use colons rather than JSX assignment syntax.

Reference

PropertyFieldButton – Official PnP SPFx Property Controls documentation

Edvaldo Guimrães Filho Avatar

Published by