### Install MkDocs Dependencies
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/build-the-doc.md
Installs the required documentation dependencies using pip from the specified requirements file. Ensure Python is installed and added to your PATH.
```bash
python -m pip install -r requirements-docs.txt
```
--------------------------------
### Install Dependencies for Search Parts
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/how-to-contribute.md
Run this command from the `search-parts` or `search-extensibility-demo` project to install necessary npm packages.
```bash
npm i
```
--------------------------------
### Build Project
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/how-to-contribute.md
Use this command to build the SPFx project after installing dependencies or making changes.
```bash
npm run build
```
--------------------------------
### Serve the Solution Locally
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/how-to-contribute.md
Start the SPFx Fast Serve Tool from the `search-parts` folder to enable quick development and debugging.
```bash
npm run serve
```
--------------------------------
### Serve Documentation Locally
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/build-the-doc.md
Starts a local web server to preview the documentation. Open your browser to the provided address to see the changes.
```bash
python -m mkdocs serve
```
--------------------------------
### Template Example with Item Selection
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
This example shows how to apply a 'selected' CSS class to items based on their selection state using the `isItemSelected` Handlebars helper. It also demonstrates the use of `data-is-focusable`, `data-selection-index`, and `data-selection-toggle` attributes for item selection functionality.
```html
...
{{#each data.items as |item|}}
{{slot item @root.slots.Title}}
{{/each}}
```
--------------------------------
### Example Filter URL
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/scenarios/clickable-column-filter-link.md
This is an example of a URL with a filter parameter appended to the query string. This format is used when a user clicks a filter link.
```url
https://yourtenant.sharepoint.com/sites/YourSite?f_4b43307b-8891-42d5-9a62-7bfb83c11de9=%5B%7B%22filterName%22%3A%22RefinableString109%22%2C%22values%22%3A%5B%7B%22name%22%3A%22Trouble%20Brewing%22%2C%22value%22%3A%22%5C%22%C7%82%C7%8254726f75626c652042726577696e67%5C%22%22%2C%22operator%22%3A0%2C%22disabled%22%3Afalse%7D%5D%2C%22operator%22%3A%22or%22%7D%5D#
```
--------------------------------
### Debug Library Component with Heft
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/index.md
Use 'heft start' to debug library components with SPFx v1.22 and later.
```console
heft start
```
--------------------------------
### Get Summary with getSummary
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
Returns the formatted value for rendering, typically used with the HitHighlightedSummary managed property from SharePoint Search.
```handlebars
{{getSummary "HitHighlightedSummary"}}
```
--------------------------------
### Register Custom Layout Information
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/custom_layout.md
Implement the `getCustomLayouts()` method in your `IExtensibilityLibrary` to return an array of `ILayoutDefinition` objects. This example shows how to define a custom layout with HTML and Handlebars rendering.
```typescript
public getCustomLayouts(): ILayoutDefinition[] {
/* eslint-disable @typescript-eslint/no-var-requires */
return [
{
name: 'My custom layout',
iconName: 'Color',
key: 'CustomLayout',
type: LayoutType.Results,
renderType: LayoutRenderType.Handlebars,
templateContent: require('../custom-layout.html').default.toString(),
serviceKey: ServiceKey.create('MyCompany:CustomLayout', Customlayout)
}
];
}
```
--------------------------------
### Deep Link Example for Search Filters
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
Constructs a deep link to a search results page with a pre-selected filter. Requires the Search Filters web part instance ID and correctly formatted hex-encoded values.
```handlebars
{{!-- Example for a Search Filters web part whose instance ID is known --}}
{{!-- Replace the GUID below with the instance ID of your Search Filters web part --}}
{{slot item @root.slots.Title}}
```
--------------------------------
### Optional Generic Typing for DataSource
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/search-extensibility/README.md
Use optional generic typing for `BaseDataSource` to get full intellisense for `this.context`. This is recommended for new code.
```typescript
import {
BaseDataSource
} from '@pnp/modern-search-extensibility';
import {
WebPartContext
} from '@microsoft/sp-webpart-base';
export class MyDataSource extends BaseDataSource {
public async getData() {
// `this.context` is now typed as WebPartContext — full intellisense
const url = this.context.pageContext.web.absoluteUrl;
// ...
}
}
```
--------------------------------
### Define Custom Adaptive Card Actions
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/adaptivecards_customizations.md
Define custom actions within your Adaptive Card's JSON structure. This example shows how to include 'Action.Submit' and 'Action.OpenUrl' actions, which can then be handled by the `invokeCardAction` method in your TypeScript implementation.
```json
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.3",
"body": [
{
"type": "TextBlock",
"text": "**${$root.data.totalItemsCount}** results",
"size": "Medium",
"wrap": true,
"$when": "${$root.properties.showResultsCount == true}"
},
{
"type": "Container",
"$data": "${data.items}",
"items": [
{
"type": "ColumnSet",
"id": "${hitId}",
"columns": [
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"wrap": true,
"text": ""
}
],
"width": "auto"
},
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"wrap": true,
"text": "[${string(jPath($data, concat('.',$root.slots['Title']))[0])}](${string(jPath($data, concat('.',$root.slots['Path']))[0])})"
}
],
"width": "auto"
},
{
"type": "Column",
"items": [
{
"type": "ActionSet",
"actions": [
{
"type": "Action.Submit",
"title": "Click on item",
"style": "positive",
"data": {
"id": "123"
}
}
],
"spacing": "medium"
}
],
"width": "auto"
}
]
}
]
}
],
"actions": [
{
"type": "Action.Submit",
"title": "Global click",
"data": {
"id": "456"
}
},
{
"type": "Action.OpenUrl",
"title": "Open URL",
"url": "https://pnp.github.io/"
}
]
}
```
--------------------------------
### getUniqueCount
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
Get the unique count of a property over the result set or get the unique count of objects in an array.
```APIDOC
## getUniqueCount
### Description
Get the unique count of a property over the result set (or another array) or get the unique count of objects in an array. Example: [1,1,1,2,2,4] would return `3`.
### Example
`{{getUniqueCount [1,1,1,2,2,4]}}`
```
--------------------------------
### Deploy Documentation
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/build-the-doc.md
Deploys the documentation using MkDocs. This command is typically run from the main branch.
```bash
python -m mkdocs gh-deploy
```
--------------------------------
### Create Symbolic Link for Search Extensibility
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/how-to-contribute.md
After building the `search-extensibility` project, run this command to create a symbolic link for local development.
```bash
npm link
```
--------------------------------
### Get Tag Name with getTagName
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
Extracts the name of a tag from a tag field string, removing the ID component.
```handlebars
{{getTagName "Tag"}}
```
--------------------------------
### Build Project with Gulp
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/how-to-contribute.md
An alternative command to build the SPFx project using Gulp.
```bash
gulp bundle
```
--------------------------------
### Build and package with Gulp
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/index.md
Bundle and package the SPFx solution for deployment using the Gulp build toolchain. This command generates the necessary files for the app catalog.
```bash
gulp bundle --ship
gulp package-solution --ship
```
--------------------------------
### Get URL Parameter
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
Extracts a query parameter value from a URL. If the URL is omitted, the current browser URL is used.
```handlebars
{{getUrlParameter "k"}}
```
```handlebars
{{getUrlParameter "k" "https://foo?k=test"}}
```
--------------------------------
### Get Unique Items with getUnique
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
Returns a new array containing only the unique values of a specified property from an array of objects.
```handlebars
{{getUnique items "NewsCategory"}}
```
--------------------------------
### Display a Panel with Content
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/web_components_list.md
Use `` to display a collapsible panel. Configure its appearance and behavior using attributes like `data-is-open`, `data-is-light-dismiss`, `data-is-blocking`, `data-size`, and `data-panel-header-text`. Content for the panel trigger and the panel itself are defined within `` tags with IDs `panel-open` and `panel-content` respectively.
```html
Click me!
Panel content!
```
--------------------------------
### Display file preview with pnp-filepreview
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/web_components_list.md
Wrap any element with pnp-filepreview to show a file preview in a callout when the element is clicked. Provide URLs for the preview image and the preview content.
```html
Wrapped element
```
--------------------------------
### Commented-out Conditional Logic
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/search-parts/src/layouts/results/detailsList/details-list.html
Example of commented-out Handlebars logic for showing the results count. This is not active but shows a potential configuration.
```handlebars
{{!-- #if showResultsCount --}}
{{!-- /if --}}
```
--------------------------------
### Implement BaseSuggestionProvider (Recommended with SPFx Context)
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/custom_suggestions_provider.md
Extend `BaseSuggestionProvider` with full SPFx type safety for the Web Part context, enabling better intellisense.
```typescript
import { WebPartContext } from '@microsoft/sp-webpart-base';
export class CustomSuggestionProvider extends BaseSuggestionProvider {
// this.context now has full WebPartContext intellisense
...
}
```
--------------------------------
### Get Unique Count with getUniqueCount
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
Calculates the number of unique values within an array or for a specific property across a set of items.
```handlebars
{{getUniqueCount [1,1,1,2,2,4]}}
```
--------------------------------
### Link Local Search Extensibility Package
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/how-to-contribute.md
If you made local changes to `search-extensibility`, use this command after `npm i` in other projects to link your local version.
```bash
npm link @pnp/modern-search-extensibility
```
--------------------------------
### Create Placeholders with Shimmer Effects
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
Implement loading placeholders (shimmers) using the `` section and CSS classes like `placeholder--shimmer` and `placeholder--line`.
```html
```
--------------------------------
### Implement BaseSuggestionProvider (Basic)
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/custom_suggestions_provider.md
Extend the `BaseSuggestionProvider` abstract class for basic implementation without SPFx type safety on the context.
```typescript
export class CustomSuggestionProvider extends BaseSuggestionProvider {
...
}
```
--------------------------------
### URL Parse
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
Parses a URL to extract its components. Omitting the URL uses the current browser URL. Used here to get the query part.
```handlebars
{{get "query" (urlParse)}}
```
--------------------------------
### Get User Email
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
Extracts a user's email address from a string using an email regex. Returns the first match found.
```handlebars
{{getUserEmail "franck.cornu@contoso.onmicrosoft.com | Franck Cornu | 693A30232E667C6D656D626572736869707C6672616E636B2E636F726E7540616571756F736465762E6F6E6D6963726F736F66742E636F6D i:0#.f|membership|franck.cornu@contoso.onmicrosoft.com"}}
```
--------------------------------
### Format Date with getDate
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
Format a date string according to the current language and specified time handling. The input date should be in ISO 8601 format.
```handlebars
{{getDate 2018-09-10T06:29:25.0000000Z 'LL'}}
```
--------------------------------
### Get URL Field
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
Use with SharePoint Search data source to retrieve the URL or Title part of a URL auto-created managed property.
```handlebars
{{getUrlField MyPropertyOWSURLH "Title"}}
```
--------------------------------
### Build and package with Heft
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/index.md
Bundle and package the SPFx solution for deployment using the Heft build toolchain. This command generates the necessary files for the app catalog.
```bash
heft build --production
heft package-solution --production
```
--------------------------------
### Get Attachments with getAttachments
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
Return object structure for list item attachments. This helper is used within a block to process attachment data.
```handlebars
{{#getAttachments LinkOfficeChild}}{{index}} - {{fileName}}{{/getAttachments}}
```
--------------------------------
### Define Template and Placeholder Content
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
Use 'template' for fetched data display and 'placeholder' for loading states. Both support Handlebars and web components.
```html
```
--------------------------------
### Get SPFx Page Context Property with getPageContext
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
Retrieve a specific property from the SPFx context object, such as user display name or UI culture.
```handlebars
{{getPageContext "user.displayName"}}
```
```handlebars
{{getPageContext "cultureInfo.currentUICultureName"}}
```
--------------------------------
### Render a file icon with pnp-iconfile
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/web_components_list.md
Use the pnp-iconfile component to display a file icon based on its extension. Set data-is-container to true to render a folder icon. Specify the desired icon size.
```html
```
--------------------------------
### Get Graph Preview URL with getGraphPreviewUrl
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
Generate a Microsoft Graph preview URL from an absolute URL. This is useful for creating links to documents within SharePoint.
```handlebars
{{getGraphPreviewUrl "https://contoso.sharepoint.com/sites/dev/Shared%20Documents/MyDocument.pdf"}}
```
--------------------------------
### Handle Theme Argument in Handlebars Helper
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/handlebars_customizations.md
Inside the helper, access the theme object from `options.hash.theme`. This example extracts the primary theme color or falls back to a default.
```typescript
namespace.registerHelper('myColoredBadge', function (text: string, options: any) {
// Accept either a Fluent-style theme object or a plain palette object,
// fall back to a hardcoded color if no theme was passed.
const palette = (options?.hash?.theme?.palette) || options?.hash?.theme || {};
const accent = palette.themePrimary || '#0078d4';
const safeText = namespace.Utils.escapeExpression(text);
return new namespace.SafeString(
`${safeText}`
);
});
```
--------------------------------
### Access SharePoint Framework Context and Services
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/index.md
Implement a custom library component using the Service Locator pattern to access SPFx context and services. Ensure you import necessary packages like '@microsoft/sp-core-library'.
```typescript
import { IAdaptiveCardAction, IComponentDefinition, IExtensibilityLibrary, ILayoutDefinition, ISuggestionProviderDefinition, IQueryModifierDefinition, IDataSourceDefinition, IDataSource } from '@pnp/modern-search-extensibility';
import { ServiceKey, ServiceScope } from '@microsoft/sp-core-library';
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
import { PageContext } from '@microsoft/sp-page-context';
export class MyCustomLibraryComponent implements IExtensibilityLibrary {
public static readonly serviceKey: ServiceKey =
ServiceKey.create('SPFx:MyCustomLibraryComponent', MyCustomLibraryComponent);
private _spHttpClient: SPHttpClient;
private _pageContext: PageContext;
private _currentWebUrl: string;
constructor(serviceScope: ServiceScope) {
serviceScope.whenFinished(() => {
this._spHttpClient = serviceScope.consume(SPHttpClient.serviceKey);
this._pageContext = serviceScope.consume(PageContext.serviceKey);
this._currentWebUrl = this._pageContext.web.absoluteUrl;
});
}
public getCustomLayouts(): ILayoutDefinition[] {
return [];
}
public getCustomWebComponents(): IComponentDefinition[] {
return [];
}
public getCustomSuggestionProviders(): ISuggestionProviderDefinition[] {
return [];
}
public registerHandlebarsCustomizations?(handlebarsNamespace: typeof Handlebars): void {
}
public getCustomQueryModifiers?(): IQueryModifierDefinition[]{
}
public invokeCardAction(action: IAdaptiveCardAction): void {
// Process the action based on type
if (action.type == "Action.OpenUrl") {
window.open(action.url, "_blank");
} else if (action.type == "Action.Submit") {
// Process the Submit action based on title
switch (action.title.toLowerCase()) {
case "user":
// Invoke the currentUser endpoint
this._spHttpClient.get(
`${this._currentWebUrl}/_api/web/currentUser`,
SPHttpClient.configurations.v1,
null).then((response: SPHttpClientResponse) => {
return response.json();
});
break;
default:
console.log('Action not supported!');
break;
}
}
}
public getCustomDataSources(): IDataSourceDefinition[]
{
return [
{
name: 'Custom Data Source',
iconName: 'Database',
key: 'CustomDataSource',
serviceKey: ServiceKey.create('CustomDataSource', CustomDataSource)
}
];
}
public name(): string {
return 'MyCustomLibraryComponent';
}
}
```
--------------------------------
### Configure html-loader with Gulp
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/index.md
Configure webpack to use html-loader without minification for Handlebars templates when using the Gulp toolchain. This involves merging configuration changes into the existing webpack setup.
```javascript
const envCheck = build.subTask('environmentCheck', (gulp, config, done) => {
build.configureWebpack.mergeConfig({
additionalConfiguration: (generatedConfiguration) => {
// Remove the default html rule
generatedConfiguration.module.rules = generatedConfiguration.module.rules.filter(rule => {
return rule.test.toString() !== '/\.html$/';
});
// Add html loader without minimize so that we can use it for handlebars templates
generatedConfiguration.module.rules.push({
test: /\.html$/,
loader: 'html-loader',
options: {
minimize: false
}
});
return generatedConfiguration;
}
});
done();
});
build.rig.addPreBuildTask(envCheck);
```
--------------------------------
### Debug Library Component with Gulp
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/index.md
Use 'gulp serve' to debug library components with SPFx v1.21.1 and earlier.
```console
gulp serve
```
--------------------------------
### Format Dates with dayjs/moment
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
Format dates using dayjs or moment.js. The 'moment' helper is for backward compatibility. Use hash arguments for explicit output formats like Unix timestamps.
```handlebars
{{dayjs "X"}}
```
```handlebars
{{dayjs "2026-10-01" "LL"}}
```
```handlebars
{{dayjs someDate "YYYY-MM-DD"}}
```
```handlebars
{{dayjs someDate "X"}}
```
```handlebars
{{dayjs 1714400000 "X"}}
```
```handlebars
{{dayjs someDate format="X"}}
```
```handlebars
{{dayjs format="LL" subtract="7 days"}}
```
--------------------------------
### Implement BaseDataSource (Basic)
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/custom_data_sources.md
Implement the `BaseDataSource` abstract class for basic data source logic without SPFx type safety on the context.
```typescript
export class CustomDataSource extends BaseDataSource {
...
}
```
--------------------------------
### dayjs / moment
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/extensibility/templating.md
Format dates using dayjs. The moment helper name is kept for backward compatibility. Both names are identical.
```APIDOC
## dayjs / moment
### Description
Format dates using [dayjs](https://day.js.org/docs/en/display/format). The `moment` helper name is kept for backward compatibility. Both names are identical.
When the second positional argument is `"X"` or `"x"` and the first argument is numeric, it is treated as an **input parsing hint** (`"X"` = Unix seconds, `"x"` = Unix milliseconds) rather than an output format. To output Unix seconds/milliseconds from a date value, use the `format` hash argument instead: `{{dayjs someDate format="X"}}`.
- `{{dayjs "X"}}` — current date as Unix seconds
- `{{dayjs "2026-10-01" "LL"}}` — parse date, format as locale date
- `{{dayjs someDate "YYYY-MM-DD"}}` — parse date value, custom format
- `{{dayjs someDate "X"}}` — format date as Unix seconds (non-numeric first arg = output format)
- `{{dayjs 1714400000 "X"}}` — parse `1714400000` as Unix seconds (numeric first arg = input parsing)
- `{{dayjs someDate format="X"}}` — format date as Unix seconds (explicit hash format)
- `{{dayjs format="LL" subtract="7 days"}}` — 7 days ago, locale format
Supported output format tokens include all [dayjs format tokens](https://day.js.org/docs/en/display/format) plus `X` (Unix seconds), `x` (Unix milliseconds), `Do` (ordinal day), `Q` (quarter), `k`/`kk` (hour 1-24), `w`/`ww` (locale week), `W`/`WW` (ISO week), `e` (locale weekday), `E` (ISO weekday), `gg`/`gggg` and `GG`/`GGGG` (week year).
### Example
`{{dayjs "X"}}` returns the current date as a Unix timestamp (seconds).
```
--------------------------------
### Configure PnP Panel for Info Panel
Source: https://github.com/microsoft-search/pnp-modern-search/blob/main/docs/scenarios/more-info-panel-with-pnp-panel.md
This HTML snippet configures a PnP panel to display detailed information when a user clicks an info icon next to a search result. It includes templates for opening the panel and displaying content like document links and metadata.
```html