Overview

Search has been an integral part of SharePoint over the years. Search helps us to get the security trimmed results across the tenant. Unfortunately, modern SharePoint sites do not provide the search related web parts by default.

In this article, we will explore to query the Search REST API and get the results. We will use React JS in this example.

Create SPFx Solution

Open the command prompt. Create a directory for SPFx solution.
  1. md spfx-react-search
Navigate the above-created directory.
  1. cd spfx-react-search
Run Yeoman SharePoint Generator to create the solution.
  1. yo @microsoft/sharepoint
Yeoman generator will present you with the wizard by asking questions about the solution to be created.

Create SPFx Solution
Solution Name

Hit enter to have the default name (spfx-react-search in this case) or type in any other name for your solution.
Selected choice - Hit enter
Target for component

Here, we can select the target environment where we are planning to deploy the client web part i.e. SharePoint Online or SharePoint OnPremise (SharePoint 2016 onwards).
Selected choice - SharePoint Online only (latest)
Place of files

We may choose to use the same folder or create a subfolder for our solution.
Selected choice - Same folder
Deployment option

Selecting Y will allow the app to deployed instantly to all sites and will be accessible everywhere.
Selected choice - N (install on each site explicitly)
Type of client-side component to create

We can choose to create client-side webpart or an extension. Choose the webpart option.
Selected choice - WebPart
Web part name

Hit enter to select the default name or type in any other name.
Selected choice - SearchResultsViewer
Web part description

Hit enter to select the default description or type in any other value.
Selected choice - Retrieve search results using the REST API.
Framework to use

Select any JavaScript framework to develop the component. Available choices are (No JavaScript Framework, React, and Knockout)
Selected choice - React

Model for Search Results

We will define an interface for representing the SharePoint search results.

Add an interface (ISPSearchResult.ts) representing the SharePoint search result.
  1. export interface ISPSearchResult
  2. {
  3. Title: string;
  4. Description: string;
  5. Url: string
  6. }
React JS acts on the state change. Let us add state to our solution (ISearchResultsViewerState.ts)
  1. import {ISPSearchResult} from './ISPSearchResult';
  2. export interface ISearchResultsViewerState {
  3. status: string;
  4. searchText: string;
  5. items: ISPSearchResult[];
  6. }
Model for Search Results
Configure SearchResultsViewer.tsx for this state.

Configure SearchResultsViewer.tsx

Add Controls to WebPart

Open SearchResultsViewer.tsx under “\src\webparts\searchResultsViewer\components\” folder.

Modify the Render method to include the required controls.
Text field
  1. // Import Textfield component
  2. import { TextField } from 'office-ui-fabric-react/lib/TextField';
  3. import './TextField.Examples.scss';
  4. <TextField
  5. required={true}
  6. name="txtSearchText"
  7. placeholder="Search..."
  8. value={this.state.searchText}
  9. onChanged={e => this.setState({ searchText: e })}
  10. />
Button
  1. // Import Button component
  2. import { IButtonProps, DefaultButton } from 'office-ui-fabric-react/lib/Button';
  3. <DefaultButton
  4. data-automation-id="search"
  5. target="_blank"
  6. title="Search"
  7. onClick={this._searchClicked}
  8. >
  9. Search
  10. </DefaultButton>
  11. private _searchClicked(): void {
  12. }
List
  1. // Import List component
  2. import { FocusZone, FocusZoneDirection } from 'office-ui-fabric-react/lib/FocusZone';
  3. import { List } from 'office-ui-fabric-react/lib/List';
  4. // Import Link component
  5. import { Link } from 'office-ui-fabric-react/lib/Link';
  6. <FocusZone direction={FocusZoneDirection.vertical}>
  7. <div className="ms-ListGhostingExample-container" data-is-scrollable={true}>
  8. <List items={this.state.items} onRenderCell={this._onRenderCell} />
  9. </div>
  10. </FocusZone>
  11. private _onRenderCell(item: ISPSearchResult, index: number, isScrolling: boolean): JSX.Element {
  12. return (
  13. <div className="ms-ListGhostingExample-itemCell" data-is-focusable={true}>
  14. <div className="ms-ListGhostingExample-itemContent">
  15. <div className="ms-ListGhostingExample-itemName">
  16. <Link href={item.Url}>{item.Title}</Link>
  17. </div>
  18. <div className="ms-ListGhostingExample-itemName">{item.Description}</div>
  19. <p></p>
  20. </div>
  21. </div>
  22. );
  23. }
In the command prompt type “gulp serve” to see the controls on webpart.
command prompt type “gulp serve”
Implement service to retrieve search results
  1. Add a folder “services” to the solution.
  2. To the “services” folder, add a file SearchService.ts
  3. We will use the REST API to get the search results.
  1. import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
  2. import { IWebPartContext } from '@microsoft/sp-webpart-base';
  3. import { ISPSearchResult } from '../components/ISPSearchResult';
  4. import { ISearchResults, ICells, ICellValue, ISearchResponse } from './ISearchService';
  5. import { escape } from '@microsoft/sp-lodash-subset';
  6. export default class SearchService {
  7. constructor(private _context: IWebPartContext) {
  8. }
  9. public getSearchResults(query: string): Promise<ISPSearchResult[]> {
  10. let url: string = this._context.pageContext.web.absoluteUrl + "/_api/search/query?querytext='" + query + "'";
  11. return new Promise<ISPSearchResult[]>((resolve, reject) => {
  12. // Do an Ajax call to receive the search results
  13. this._getSearchData(url).then((res: ISearchResults) => {
  14. let searchResp: ISPSearchResult[] = [];
  15. // Check if there was an error
  16. if (typeof res["odata.error"] !== "undefined") {
  17. if (typeof res["odata.error"]["message"] !== "undefined") {
  18. Promise.reject(res["odata.error"]["message"].value);
  19. return;
  20. }
  21. }
  22. if (!this._isNull(res)) {
  23. const fields: string = "Title,Path,Description";
  24. // Retrieve all the table rows
  25. if (typeof res.PrimaryQueryResult.RelevantResults.Table !== 'undefined') {
  26. if (typeof res.PrimaryQueryResult.RelevantResults.Table.Rows !== 'undefined') {
  27. searchResp = this._setSearchResults(res.PrimaryQueryResult.RelevantResults.Table.Rows, fields);
  28. }
  29. }
  30. }
  31. // Return the retrieved result set
  32. resolve(searchResp);
  33. });
  34. });
  35. }
  36. /**
  37. * Retrieve the results from the search API
  38. *
  39. * @param url
  40. */
  41. private _getSearchData(url: string): Promise<ISearchResults> {
  42. return this._context.spHttpClient.get(url, SPHttpClient.configurations.v1, {
  43. headers: {
  44. 'odata-version': '3.0'
  45. }
  46. }).then((res: SPHttpClientResponse) => {
  47. return res.json();
  48. }).catch(error => {
  49. return Promise.reject(JSON.stringify(error));
  50. });
  51. }
  52. /**
  53. * Set the current set of search results
  54. *
  55. * @param crntResults
  56. * @param fields
  57. */
  58. private _setSearchResults(crntResults: ICells[], fields: string): any[] {
  59. const temp: any[] = [];
  60. if (crntResults.length > 0) {
  61. const flds: string[] = fields.toLowerCase().split(',');
  62. crntResults.forEach((result) => {
  63. // Create a temp value
  64. var val: Object = {}
  65. result.Cells.forEach((cell: ICellValue) => {
  66. if (flds.indexOf(cell.Key.toLowerCase()) !== -1) {
  67. // Add key and value to temp value
  68. val[cell.Key] = cell.Value;
  69. }
  70. });
  71. // Push this to the temp array
  72. temp.push(val);
  73. });
  74. }
  75. return temp;
  76. }
  77. /**
  78. * Check if the value is null or undefined
  79. *
  80. * @param value
  81. */
  82. private _isNull(value: any): boolean {
  83. return value === null || typeof value === "undefined";
  84. }
  85. }
Test the WebPart
  1. On the command prompt, type “gulp serve”
  2. Open SharePoint site
  3. Navigate to /_layouts/15/workbench.aspx
  4. Add the webpart to page.
  5. Type the search text and verify the search results.
Test the WebPart
Troubleshooting

In some cases, SharePoint workbench (https://[tenant].sharepoint.com/_layouts/15/workbench.aspx) shows below error although “gulp serve” is running.

Troubleshooting

Open below URL in the next tab of the browser. Accept the warning message.

https://localhost:4321/temp/manifests.js

Summary

Modern SharePoint does not have web part for displaying search results. However, using the search REST API the results can be achieved.