By admin • August 5, 2025
When building content-heavy websites with Sitecore XM Cloud, one of the biggest challenges is helping users discover related content that truly matches their interests. Traditional search approaches often fall short because they rely on basic keyword matching or simple taxonomy filters. We needed a more intelligent solution that could understand content relationships and prioritize the most relevant items.
That's how we developed our Related Content Widget using Sitecore Search's powerful custom combined_tags
field and custom client-side sorting logic. This approach allows us to create highly personalized content recommendations that adapt to each page's context.
The Challenge
Our content editors were struggling with several issues:
- Related content wasn't contextually relevant to the current page
- Search results were ordered by basic relevance, not by actual content relationships
- No way to prioritize specific content items when needed
- Limited flexibility for different content types and use cases
We needed a solution that could understand content relationships through multiple taxonomy fields and provide intelligent sorting based on the number of matching tags.
Sitecore Search and the Combined Tags Field
Sitecore Search provides a powerful search API that can be extended with custom fields. Our solution leverages a custom combined_tags
attribute that we populated using Sitecore Search's document extractor. This field combines all taxonomy values from different facets (applications, markets, solutions, technologies, content types, etc.) into a single searchable array.
Here's how the combined_tags
field is populated:
// Document extractor configuration for combined_tags
const combinedTags = [
...item.applications,
...item.markets,
...item.solutions,
...item.technologies,
...item.content_type,
...item.resource_type
].filter(Boolean);
The Solution: Smart Sorting with Combined Tags
Our implementation works in three key steps:
- Tag Collection: Extract all relevant tags from the current page's facets
- Search Filtering: Use
FilterAnyOf
on thecombined_tags
field to find matching content - Client-Side Sorting: Sort results by the number of matching tags, with secondary sorting by date and alphabetical order
Priority Pages Feature
One of the key features of our solution is the ability to pre-select priority pages in the component's datasource. These priority pages will always appear at the top of the results, regardless of their tag match count. This gives content editors complete control over which content should be highlighted for specific pages.
Here's how priority pages work:
// Priority pages are specified in the component datasource
const priorityPages = "item-id-1|item-id-2|item-id-3";
// These items will always appear first, in the order specified
const priorityItemIds = priorityPages.split('|').map(id => id.trim());
Implementation Details
Here's the core sorting function that powers our intelligent content recommendations:
// Helper function to sort articles by tag match count
const sortArticlesByTagMatches = (
articles: RelatedArticleModel[],
searchTags: string[],
priorityItemIds: string[] = []
): RelatedArticleModel[] => {
return [...articles].sort((a, b) => {
const aKey = a.sc_id || a.id;
const bKey = b.sc_id || b.id;
// Priority items always come first
if (priorityItemIds.length > 0) {
const aIsPriority = priorityItemIds.includes(aKey);
const bIsPriority = priorityItemIds.includes(bKey);
if (aIsPriority && !bIsPriority) return -1;
if (!aIsPriority && bIsPriority) return 1;
if (aIsPriority && bIsPriority) {
const aIndex = priorityItemIds.indexOf(aKey);
const bIndex = priorityItemIds.indexOf(bKey);
return aIndex - bIndex;
}
}
// Count matching tags for each article
const aMatchingTags = a.combined_tags?.filter(tag =>
searchTags.includes(tag)
).length || 0;
const bMatchingTags = b.combined_tags?.filter(tag =>
searchTags.includes(tag)
).length || 0;
// Sort by tag match count (descending)
if (bMatchingTags !== aMatchingTags) {
return bMatchingTags - aMatchingTags;
}
// Secondary sorting by published date (newest first)
if (a.published_date && b.published_date) {
const dateDiff = b.published_date - a.published_date;
if (dateDiff !== 0) return dateDiff;
}
// Tertiary sorting by name (alphabetical)
const aName = (a.name || a.title || '').toLowerCase();
const bName = (b.name || b.title || '').toLowerCase();
return aName.localeCompare(bName);
});
};
Search Request Configuration
Here's how we configure the search request to use combined tags:
// Extract all tags from page facets
const allTags: string[] = [];
pageFacets.forEach((facet) => {
if (facet.value && facet.value.length > 0) {
const facetValues = facet.value.map((item) => item.text);
allTags.push(...facetValues);
}
});
// Create combined_tags filter
if (allTags.length > 0) {
const combinedTagsFilter = new FilterAnyOf('combined_tags', allTags);
facetFilters.push(combinedTagsFilter);
}
Key Benefits
This approach provides several advantages:
- Contextual Relevance: Content is matched based on multiple taxonomy fields, not just keywords
- Intelligent Sorting: Results are ordered by the number of matching tags, ensuring the most relevant content appears first
- Priority Page Control: Content editors can pre-select specific pages that should always appear at the top
- Flexible Prioritization: Priority pages are shown first, followed by content sorted by tag matches
- Fallback Sorting: When tag matches are equal, content is sorted by date and then alphabetically
- Performance: Client-side sorting is fast and doesn't require additional API calls
Usage Example
Here's how to use the Related Content Widget in your Sitecore XM Cloud pages:
<RelatedWidgetComponent
sources={['your-source-id']}
templateID={['your-template-id']}
pageFacets={pageFacets}
limit={6}
excludeItemId={currentItemId}
/>
Conclusion
This intelligent related content widget can be configured to automatically retrieve the current page template and its tags, then render related items without any changes in the datasource. The component intelligently analyzes the page's taxonomy facets and finds the most relevant content based on tag matching.
The same code can be used with different variants to achieve various visual presentations, such as 3-card grids, 2-card layouts, or carousel views. This flexibility allows content editors to choose the most appropriate display format for their content while maintaining the same intelligent sorting and filtering logic.
This solution has significantly improved content discovery on our Sitecore XM Cloud sites, providing users with highly relevant recommendations that adapt to each page's context while giving content editors complete control over priority content and display options.