![]()
PnP Modern Search Web Part: In this tutorial, we will learn about how to develop a SharePoint Online custom search web part using the PnP JS and SharePoint Framework (SPFx).
Key-Highlights: Develop custom search web part using PnP JS
- What is PnP Modern Search Web Part in SharePoint Online?
- Project scaffolding process
- Launch Visual Studio editor
- SearchService.ts file code
- SpFxSearchWebPart.ts file (web part main file) code
- Demo โ SPFx framework search results using the PnP JS
What is PnP Modern Search Web Part in SharePoint Online?
Introduction
The PnP Modern Search Web Part is an innovative, open-source solution developed by the SharePoint Patterns and Practices (PnP) community. This web part significantly enhances the default search capabilities in SharePoint Online, offering users a highly customizable and powerful search experience. In this article, weโll explore its features, benefits, and how to get started with the PnP Modern Search Web Part.
Key Features
- Customizable Search Box
- The search box component allows users to input their search queries with ease. It can be customized to fit the look and feel of your SharePoint site.
- Advanced Search Results
- The search results web part displays search results in a variety of customizable layouts. Users can tailor the display to show the most relevant information, such as title, summary, and metadata.
- Refiners and Filters
- Refiners and filters help narrow down search results based on specific criteria, such as content type, date, or custom metadata. This functionality makes it easier for users to find exactly what they need.
- Search Verticals
- Search verticals provide different search experiences for various types of content. For example, you can set up separate verticals for documents, people, or news, each with its own tailored search results page.
- Connectors and Data Sources
- The PnP Modern Search Web Part supports integration with multiple data sources, including SharePoint lists, libraries, and external sources. This allows for a unified search experience across different repositories.
Benefits
- Enhanced User Experience
- With its advanced features and customization options, the PnP Modern Search Web Part significantly improves the user experience by providing more relevant and accessible search results.
- Flexibility and Customization
- The web partโs flexibility allows administrators and developers to customize the search experience to meet the specific needs of their organization.
- Open Source and Community-Driven
- Being an open-source project, it benefits from continuous improvements and updates from the PnP community, ensuring it stays current with the latest trends and requirements.
- Seamless Integration
- The web part integrates seamlessly with modern SharePoint sites, enhancing their functionality without compromising performance.
Getting Started
- Installation
- To get started, download the PnP Modern Search Web Part from the GitHub repository and follow the installation instructions provided.
- Configuration
- Once installed, configure the web part according to your requirements. This includes setting up the search box, search results layout, refiners, filters, and connecting to data sources.
- Customization
- Customize the web part using the extensive configuration options available. You can modify the appearance, behavior, and functionality to match your organizational needs.
- Deployment
- Deploy the configured web part to your SharePoint Online site. Ensure that it meets your performance and usability standards through thorough testing.
Project scaffolding process: Develop custom search web part using PnP JS
Create a folder name โSPFx_Searchโ.
Navigate to the above created folder.

Enter the below parameters when asked:
Let's create a new SharePoint solution. ? What is your solution name? sp-fx-search ? Which baseline packages do you want to target for your component(s)? SharePoint Online only (latest) ? Where do you want to place the files? Use the current folder Found npm version 6.14.7 ? Do you want to allow the tenant admin the choice of being able to deploy the solution to all sites immediately without running any feature deployment or adding apps in sites? No ? Will the components in the solution require permissions to access web APIs that are unique and not shared with other c omponents in the tenant? No ? Which type of client-side component to create? WebPart Add new Web part to solution sp-fx-search. ? What is your Web part name? SPFx_Search ? What is your Web part description? SPFx_Search description ? Which framework would you like to use? (Use arrow keys) > No JavaScript framework React Knockout

Then we will get the below screen:

We will need to install SharePoint PnP and Office UI Fabric React to our project, now run the following commands in sequence:
npm install sp-pnp-js --save npm install office-ui-fabric-react --save
The above two commands will install and save the required files in the project. We can see all dependencies in the package.json file.
"dependencies": {
"@microsoft/sp-client-base": "~1.0.0",
"@microsoft/sp-core-library": "~1.0.0",
"@microsoft/sp-webpart-base": "~1.0.0",
"@types/webpack-env": ">=1.12.1 <1.14.0",
"office-ui-fabric": "^2.6.3",
"sp-pnp-js": "^2.0.2"
},
Launch Visual Studio editor: Develop custom search web part using PnP JS and SPFx
Open the project code in the visual studio editor by typing the โcode .โ command.
Create SearchService.ts file with the two classes MockSearchService (for the mockup search test)ย and SearchService (for actual Sharepoint Online search test).

SearchService.ts file code:
'useย strict';
importย *ย asย pnpย fromย 'sp-pnp-js';
exportย interfaceย ISearchResult
{
ย ย ย ย linkย :ย string;
ย ย ย ย titleย :ย string;
ย ย ย ย descriptionย :ย string;
ย ย ย ย author:string;
}
exportย interfaceย ISearchService
{
ย ย ย ย GetMockSearchResults(query:string)ย :ย Promise<ISearchResult[]>;
}
export class MockSearchService implements ISearchService
{
ย ย ย ย public GetMockSearchResults(query:string) : Promise<ISearchResult[]>{
ย ย ย ย ย ย ย ย return new Promise<ISearchResult[]>((resolve,reject) => {
ย ย ย ย ย ย ย
ย ย ย ย ย ย resolve([
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย {title:'This is test title 1',description:'Test Title 1 description',link:'globalsharepoint2020.sharepoint.com Jump ',author:'Global SharePoint1'},
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย {title:'This is test title 2',description:'Test Title 2 description',link:'globalsharepoint2020.sharepoint.comJump ',author:'Global SharePoint2'},
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ]);
ย ย ย ย ย ย ย ย });
ย ย ย ย }
}
ย ย
export class SearchService implements ISearchService
{
ย ย ย ย publicย GetMockSearchResults(query:string)ย :ย Promise<ISearchResult[]>{
ย ย ย ย ย ย ย const _results:ISearchResult[] = [];ย ย
ย ย ย ย ย ย ย ย returnย newย Promise<ISearchResult[]>((resolve,reject)ย =>ย {
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย pnp.sp.search({
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย Querytext:query,
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย RowLimit:20,
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย StartRow:0
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย })
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย .then((results)ย =>ย {
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย results.PrimarySearchResults.forEach((result)=>{
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย _results.push({
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย title:result.Title,
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย description:result.HitHighlightedSummary,
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย link:result.Path,
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย author:result.Author
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย });
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย });
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย })
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย .then(
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ()ย =>ย {ย resolve(_results);}
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย )
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย .catch(
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ()ย =>ย {reject(newย Error("Error"));ย }
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย );
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย
ย ย ย ย ย ย ย ย });
ย ย ย ย }
}
Now open the SpFxSearchWebPart.ts file (web part main file):
SpFxSearchWebPart.ts file (web part main file)code:
'useย strict';
importย {ย Version,Environment,EnvironmentTypeย }ย fromย '@microsoft/sp-core-library';
importย {
ย ย IPropertyPaneConfiguration,
ย ย PropertyPaneTextField
}ย fromย '@microsoft/sp-property-pane';
importย {ย BaseClientSideWebPartย }ย fromย '@microsoft/sp-webpart-base';
importย {ย escapeย }ย fromย '@microsoft/sp-lodash-subset';
importย stylesย fromย './SpFxSearchWebPart.module.scss';
importย *ย asย stringsย fromย 'SpFxSearchWebPartStrings';
importย *ย asย pnpย fromย 'sp-pnp-js';
importย *ย asย SearchServiceย fromย './Services/SearchService';
importย {SPComponentLoader}ย fromย "@microsoft/sp-loader";
exportย interfaceย ISpFxSearchWebPartProps
{
ย ย description:ย string;
}
exportย defaultย classย SpFxSearchWebPartย extendsย BaseClientSideWebPartย <ISpFxSearchWebPartProps>
ย {
ย ย publicย constructor(){
ย ย ย ย super();
ย ย ย SPComponentLoader.loadCss("https://static2.sharepointonline.com/files/fabric/office-ui-fabric-js/1.2.0/css/fabric.min.cssย Jumpย ");
ย ย ย SPComponentLoader.loadCss("https://static2.sharepointonline.com/files/fabric/office-ui-fabric-js/1.2.0/css/fabric.components.min.cssย Jumpย ");
ย ย }
ย ย publicย render():ย voidย {
ย ย
ย ย ย ย this.domElement.innerHTMLย =ย `
ย ย ย ย ย ย <divย class="${styles.spFxSearch}">
ย ย ย ย ย ย ย ย <divย class="${styles.container}">
ย ย ย ย ย ย ย ย ย ย <divย class="ms-Grid-rowย ms-bgColor-themeLightย ms-fontColor-whiteย ${styles.row}">
ย ย ย ย ย ย ย ย ย ย ย ย <divย class="ms-Grid-colย ms-u-lg10ย ms-u-xl8ย ms-u-xlPush2ย ms-u-lgPush1">
ย ย ย ย ย ย ย ย ย ย ย ย ย ย <div>
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย <spanย class="ms-font-xlย ms-fontColor-white">${escape(this.properties.description)}</span><br/>
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย <divย class="ms-Grid">
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย <divย class="ms-Grid-row">
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย <divย class="ms-Grid-colย ms-u-sm10"><inputย class="ms-TextField-field"ย id="txtInput"ย placeholder="Search..."ย /></div>
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย <divย class="ms-Grid-colย ms-u-sm2">ย <Buttonย class="ms-Buttonย ms-Button--primary"ย id="btnSearchQuerySubmit"ย type="submit"ย value="Submit">Search</Button></div>
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย </div>
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย </div>
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย <divย class="ms-Listย ms-Grid-colย ms-u-sm12"ย id="searchResultsDisplay"></div>
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย </div>
ย ย ย ย ย ย ย ย ย ย ย ย </div>
ย ย ย ย ย ย ย ย ย ย </div>ย ย ย ย ย ย ย ย
ย ย ย ย ย ย ย ย </div>
ย ย ย ย ย ย </div>`;
ย ย ย ย ย this.EventListners();
ย ย ย ย
ย ย }
ย ย privateย EventListners():void
ย ย {
ย ย ย ย constย btnSearchย =ย this.domElement.querySelector("#btnSearchQuerySubmit");ย ย ย ย
ย ย ย ย constย queryText:HTMLElementย =ย <HTMLInputElement>this.domElement.querySelector("#txtInput");
ย ย ย ย btnSearch.addEventListener('click',()ย =>ย {
ย ย ย ย ย ย ย ย ย (newย SpFxSearchWebPart()).OnChangeEvent(queryText);
ย ย ย ย });
ย ย ย }
ย ย ย ย
ย ย publicย OnChangeEvent(text:HTMLElement):void
ย ย {
ย ย ย (newย SpFxSearchWebPart()).renderSearchResults((<HTMLInputElement>text).value)
ย ย ย ย ย ย .then((html)ย =>{
ย ย ย ย ย ย ย ย constย elementย ย =ย document.getElementById("searchResultsDisplay");
ย ย ย ย ย ย ย ย element.innerHTMLย =ย html;
ย ย ย ย ย ย });
ย ย }
ย ย privateย renderSearchResults(query:string):Promise<string>
ย ย {
ย ย ย ย constย _search:SearchService.ISearchServiceย =ย Environment.typeย ==ย EnvironmentType.SharePointย ?ย newย SearchService.SearchService()ย :ย newย SearchService.MockSearchService();
ย ย ย ย letย resultsHtml:stringย =ย '';
ย ย ย ย
ย ย ย ย returnย newย Promise<string>((resolve)ย =>ย {
ย ย ย ย ย ย if(query){
ย ย ย ย ย ย ย ย ย _search.ย ย ย
ย ย ย ย ย ย ย ย ย GetMockSearchResults(query)
ย ย ย ย ย ย ย ย ย .then((results)ย =>ย {
ย ย ย ย ย ย ย ย ย ย ย ย ย results.forEach((result)ย =>ย {
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย resultsHtmlย +=ย `<divย class=""ms-ListItemย ms-Grid-colย ms-u-sm8">
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย <aย href="${result.link}"><spanย class="ms-ListItem-primaryText"ย >${result.title}</span></a>
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย <spanย class="ms-ListItem-secondaryText">${result.author}<span>
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย <spanย class="ms-ListItem-tertiaryText">${result.description}</span>
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย <spanย class="ms-ListItem-metaText">10:15a</span>
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย <divย class="ms-ListItem-actions">
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย <divย class="ms-ListItem-action"ย targerUrl="${result.link}"><iย class="ms-Iconย ms-Icon--OpenInNewWindow">
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย </i></div>
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย </div>
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย </div>`;
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย });
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย })
ย ย ย ย ย ย ย ย .then(
ย ย ย ย ย ย ย ย ย ย ()ย =>ย {
ย ย ย ย ย ย ย ย ย ย ย setTimeout(()ย =>ย {
ย ย ย ย ย ย ย ย ย ย ย ย ย constย action:HTMLCollectionOf<Element>ย =ย document.getElementsByClassName("ms-ListItem-action");
ย ย ย ย ย ย ย ย ย ย ย ย ย for(letย i=0;i<action.length;i++){
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย action[i].addEventListener('click',(e)=>{
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย window.open((e.currentTargetย asย Element).getAttribute("TargerUrl"));
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย });
ย ย ย ย ย ย ย ย ย ย ย ย ย }
ย ย ย ย ย ย ย ย ย ย ย },300);
ย ย ย ย ย ย ย ย ย ย ย ย resolve(resultsHtml);
ย ย ย ย ย ย ย ย ย ย ย }
ย ย ย ย ย ย ย ย );
ย ย ย ย ย ย }
ย ย ย ย ย ย else{
ย ย ย ย ย ย ย ย resultsHtmlย +=ย "Pleaseย provideย searchย queryย inputย inย searchbox.....";
ย ย ย ย ย ย ย ย resolve(resultsHtml);
ย ย ย ย ย ย }
ย ย ย ย });
ย ย }
ย ย 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
ย ย ย ย ย ย ย ย ย ย ย ย ย ย })
ย ย ย ย ย ย ย ย ย ย ย ย ]
ย ย ย ย ย ย ย ย ย ย }
ย ย ย ย ย ย ย ย ]
ย ย ย ย ย ย }
ย ย ย ย ]
ย ย };
}
}
ย
Demo โ SPFx framework search results using the PnP JS (PnP Modern search)
Run the web part using the gulp serve command.
Then add the web part to the workbench.html page.
Pass โTestโ as search text, then hit the โSearchโ button, we can see the search result which was defined in the SearchService.ts file as mockup data.

Now, letโs add the same web part to the SharePoint Online page, for example โ โglobalsharepoint2020.sharepoint.com/_layouts/15/workbench.aspx.โ
Then pass โGlobalโ as search text, hit on the โSearchโ button, and now we can see the search results from the actual SharePoint Online tenant which matches the โGlobalโ keyword.

Summary: Develop custom search web part using PnP JS and SPFx
Thus, in this article, we have learned about how to develop a SharePoint Online custom search web part using the PnP JS and SharePoint Framework (SPFx).
The PnP Modern Search Web Part is a powerful tool for enhancing the search capabilities of SharePoint Online. Its advanced features, customization options, and seamless integration make it an invaluable addition to any SharePoint environment. By following the best practices outlined in this article, you can leverage the full potential of this web part and ensure your content ranks well in search engine results.
Source Code: PnP modern search using SPFx
The above source code can be downloaded from here.
See Also: SharePoint Online SPFx Articles
You may also like the below SharePoint SPFx articles:
- SharePoint Online: CRUD operations using SPFx and PnP JS
- SharePoint Online: Use theme color in SharePoint SPFx framework web part
- SharePoint Online: CRUD operations using SPFx ReactJS framework
- SharePoint Online: CRUD operations using SPFx no javascript framework
- Develop your first hello world web part in sharepoint framework (SPFx)
- Understanding solution structure in SharePoint framework (SPFx)
- Create custom property in SharePoint Framework โ SPFx web part pane
- Get list items from SharePoint using SPFx framework(No Javascript Framework)
- Custom search result page in SharePoint Online โ SPFx PnP Modern Search solution
About Post Author
Discover more from Global SharePoint
Subscribe to get the latest posts sent to your email.

1 comments on โPnP Modern Search Web Part: How Do I Customize It?โ