Collaborate, Innovate, Automate

Building a Multilingual Custom Search Page with PnP Modern Search

Part 2 — Verticals, Filters, and Card Templates

Part 1 of this series covered the information architecture and prerequisites for a multilingual PnP Modern Search implementation. This part covers the actual build — adding and configuring the web parts, connecting them together, scoping results by content type, and building the card templates that control how results are displayed.

Note: This is a technical companion page to the PnP Modern Search multilingual blog series. Part 1 covers IA and prerequisites. Part 3 covers the multilingual tag resolution layer — managed property mapping, search schema configuration, and the taxId crawled property behaviour.

The Web Parts

A custom PnP Modern Search page is built from a small set of web parts that work together:

The three web parts added to the page, showing verticals across the top, filters down the left, and results in the main column

These three web parts need to be connected to each other so that selecting a vertical or filter actually changes what the results web part displays.


Adding and Connecting the Web Parts

Add all three web parts to the page in the layout you want — typically verticals across the top, filters down the left, results in the main column.

Connecting Filters to Results

Edit the Search Results web part, go to the connections configuration, and select your Search Filters web part as the data source. This ensures that when a user selects a filter value, the results web part receives the refinement and updates.

Search Results web part connections configuration, showing Search Filters selected as the data source

Connecting Verticals to Results

Same pattern — the Search Results web part needs to receive its query context from the Search Verticals web part so that switching tabs changes the scope of results shown.

Web part connections configuration panel, showing Search Results connected to Search Filters and Search Verticals

Configuring Verticals

Each vertical needs its own query template that scopes results to particular content types. For an "All", "Policies", and "Process Manual" setup, you'd configure three verticals.

Scoping by Content Type

The two options are ContentType (the display name) or ContentTypeId (the permanent identifier). ContentTypeId is the more stable choice — content type display names can be renamed by an editor without anyone realising the search scope will silently break, whereas the ID never changes.

To find a content type's ID, open the list or library settings, click into the content type, and the ID appears in the URL as ctype=0x.... Or retrieve it via PnP PowerShell:

Get-PnPContentType -List "Site Pages" | Select-Object Name, Id

The query template for a vertical scoped to Policy content uses a wildcard match on the ID, which also captures any content types that inherit from it:

ContentTypeId:0x0101009D1CB255DA76424F860D91F20E6C41180071*

In this case the All vertical includes all content types in the query template. The base template will be applied to the most generic. Other cards will highlight certain content types.


Configuring the Filters Web Part

Each filter maps to a managed property — for example, OrgClassification maps to RefinableString111 in our setup. The checkbox filter template has consistently switched labels correctly when ?Locale=fr-FR is appended to the URL, in our testing.

Note: There's a cache refresh control buried in the filter's settings panel that can help if filter values appear stale after a managed property mapping change.

Slots — Mapping Tags for Multilingual Resolution

PnP Modern Search pre-configures a set of standard slots out of the box — Title, Path, FileType, and a handful of others. For those standard fields, no additional configuration is needed.

For multilingual taxonomy tags, the Tags slot needs to be explicitly mapped. In the Search Results web part data source configuration, map the Tags slot to RefinableString111. This does two things:

Note: AutoRefinableString111 is a PnP-generated helper tied to the slot mapping. Without the Tags slot mapped to RefinableString111 and Enable Localization turned on in the data source settings, the helper will not resolve translated labels.

The Card Templates

Three card templates were built for this implementation, each visually distinguishing a different content type:

The three card templates side by side — white base card, purple-bordered Policy card, green-bordered Process Manual card
The Policy vertical's query template configuration, scoping results to the Policy content type
Template Used For Visual Treatment
Base The "All" vertical and any content type without a dedicated visual treatment White card, blue pill
Policy Policy content Purple left border
Process Manual Process manual content Green left border

The Policy and Process Manual templates use a coloured left border rather than a different layout — this keeps visual consistency across the result set while still making content type identifiable at a glance.

Title Wrapping

By default, card titles may not wrap correctly if the underlying CSS doesn't account for longer titles. The fix is straightforward — ensure the title element allows normal text wrapping rather than truncating or overflowing:

.card-title {
    white-space: normal;
    overflow: visible;
    word-break: break-word;
}

Adding a Summary/Description

To surface a short description beneath the title, map a Summary slot to the AutoSummary or Description-mapped managed property in the data source properties, then reference it in the template with a clamp to limit it to a fixed number of lines:

{{#if (slot item @root.slots.Summary)}}
<div class="card-summary">
    {{slot item @root.slots.Summary}}
</div>
{{/if}}
.card-summary {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    word-break: break-word;
}
Note: The {{#if}} guard prevents an empty gap appearing on cards that have no summary value.

Tag Pills

Rather than manually splitting the raw taxonomy value and extracting labels, {{AutoRefinableString111}} handles locale-aware label resolution automatically. It reads the term IDs stored in RefinableString111, looks up the correct translation from the Term Store for the user's current locale, and renders the pill output directly:

<div class="card-pills">
    {{AutoRefinableString111}}
</div>

This requires the Tags slot to be mapped to RefinableString111 in the data source slot configuration, and Enable Localization to be ON in the data source settings.


Result Types and Inline Snippets

Within the "All" vertical, result types allow different card templates to render automatically based on the content type of each individual result — so a Policy item shows the purple-bordered card and a Process Manual item shows the green-bordered card, all within the same mixed result list.

Choose Custom as the card type, then add a link to the base card template — these are normally stored in Site Assets.

This is configured via the resultTypes Handlebars helper wrapping the card markup, with the result type rules defined separately in the web part's result types configuration, matched by content type or another managed property condition.

Mixed result list in the All vertical, showing Policy and Process Manual cards rendering with their distinct templates side by side
Result types configuration mapping content types to their corresponding card templates
{{#> resultTypes item=item}}
    <li>
        <!-- card markup -->
    </li>
{{/resultTypes}}

Connecting the Page to Site Search

Building the page is only half the job — SharePoint needs to be told to actually use it when someone searches from the standard search box anywhere on the site, rather than only working when visited directly.

Setting the Custom Search Results Page

Go to Site Settings → Search Settings (for a single site) or Site Collection Settings → Search Settings (to apply it across the whole site collection). Under "Configure search results page settings," enter the URL of your custom search page for both:

Search Settings page showing the Site Search Results Page and Site Collection Search Results Page URL fields

Updating the Query Text to Read from the Page Environment

By default, a newly added Search Results web part only responds to its own on-page search box, ignoring whatever was typed into the header search box that redirected the user there in the first place. To fix this, the web part's query text needs to read from the URL query string parameter that SharePoint passes through on redirect.


What's Covered Here vs What Comes Next

Everything in this page uses the current, working approach:

This is a fully functional multilingual custom search experience as it stands.

Part 3 covers the full diagnostic journey of getting ows_taxId_OrgClassification indexed correctly in RefinableString111 — what can go wrong with the crawled property mapping, how to verify the index is populated, and how to resolve the common failure modes.

Related Scripts