The transformItems prop allows you to post-process search results before theyβre displayed. This is useful for normalizing data, adding computed properties, filtering results, or adapting non-standard record structures.
Basic Usage
The transformItems function receives an array of hits and must return an array of hits:
import { DocSearch } from '@docsearch/react' ;
< DocSearch
appId = "YOUR_APP_ID"
apiKey = "YOUR_SEARCH_API_KEY"
indexName = "documentation"
transformItems = { ( items ) => {
return items . map (( item ) => ({
... item ,
// Add or modify properties
}));
} }
/>
Function Signature
transformItems ?: ( items : DocSearchHit []) => DocSearchHit [];
The function receives hits from all queries (if using multiple indices) and should return an array of transformed hits.
Common Use Cases
1. Adding Computed Properties
Add custom properties to hits for use in custom components:
< DocSearch
appId = "YOUR_APP_ID"
apiKey = "YOUR_SEARCH_API_KEY"
indexName = "documentation"
transformItems = { ( items ) => {
return items . map (( item ) => {
const publishDate = new Date ( item . published_at );
const isNew = Date . now () - publishDate . getTime () < 7 * 24 * 60 * 60 * 1000 ;
return {
... item ,
isNew ,
formattedDate: publishDate . toLocaleDateString (),
};
});
} }
/>
2. Filtering Results
Remove certain results based on custom logic:
< DocSearch
appId = "YOUR_APP_ID"
apiKey = "YOUR_SEARCH_API_KEY"
indexName = "documentation"
transformItems = { ( items ) => {
// Only show results from the current version
const currentVersion = 'v2' ;
return items . filter (( item ) =>
item . version === currentVersion || ! item . version
);
} }
/>
3. Modifying URLs
Adjust result URLs to match your routing:
< DocSearch
appId = "YOUR_APP_ID"
apiKey = "YOUR_SEARCH_API_KEY"
indexName = "documentation"
transformItems = { ( items ) => {
return items . map (( item ) => ({
... item ,
url: item . url . replace ( 'https://old-domain.com' , '' ),
url_without_anchor: item . url_without_anchor . replace ( 'https://old-domain.com' , '' ),
}));
} }
/>
4. Adding Index Badges
Identify which index each result came from:
< DocSearch
appId = "YOUR_APP_ID"
apiKey = "YOUR_SEARCH_API_KEY"
indices = { [ 'docs' , 'blog' , 'api' ] }
transformItems = { ( items ) => {
return items . map (( item ) => {
const indexName = item . __autocomplete_indexName || 'unknown' ;
const indexLabels = {
docs: 'π Docs' ,
blog: 'βοΈ Blog' ,
api: 'π§ API' ,
};
return {
... item ,
hierarchy: {
... item . hierarchy ,
lvl0: ` ${ indexLabels [ indexName ] || indexName } β’ ${ item . hierarchy . lvl0 } ` ,
},
};
});
} }
/>
5. Sorting Results
Re-sort results based on custom criteria:
< DocSearch
appId = "YOUR_APP_ID"
apiKey = "YOUR_SEARCH_API_KEY"
indexName = "documentation"
transformItems = { ( items ) => {
return items . sort (( a , b ) => {
// Prioritize lvl1 results over content
if ( a . type === 'lvl1' && b . type !== 'lvl1' ) return - 1 ;
if ( a . type !== 'lvl1' && b . type === 'lvl1' ) return 1 ;
// Then sort by custom priority field
return ( b . priority || 0 ) - ( a . priority || 0 );
});
} }
/>
Adapting Non-Standard Record Structures
If your Algolia index doesnβt follow the DocSearch schema, use transformItems to map your data structure:
< DocSearch
appId = "YOUR_APP_ID"
apiKey = "YOUR_SEARCH_API_KEY"
indexName = "custom_content"
searchParameters = { {
attributesToRetrieve: [ '*' ],
attributesToSnippet: [ '*:15' ],
} }
transformItems = { ( items ) => {
return items . map (( item ) => ({
objectID: item . objectID ,
content: item . body || item . description || '' ,
url: item . domain + item . path ,
url_without_anchor: item . domain + item . path ,
type: 'content' ,
anchor: null ,
hierarchy: {
lvl0: ( item . breadcrumb || []). join ( ' βΊ ' ) || item . category || '' ,
lvl1: item . title || item . h1 || '' ,
lvl2: item . subtitle || item . h2 || '' ,
lvl3: item . h3 || null ,
lvl4: null ,
lvl5: null ,
lvl6: null ,
},
_highlightResult: item . _highlightResult ,
_snippetResult: item . _snippetResult ,
}));
} }
/>
When transforming records to match the DocSearch structure, ensure you include all required fields: objectID, content, url, url_without_anchor, type, anchor, hierarchy, _highlightResult, and _snippetResult.
Required DocSearchHit Structure
type DocSearchHit = {
objectID : string ;
content : string | null ;
url : string ;
url_without_anchor : string ;
type : 'askAI' | 'content' | 'lvl0' | 'lvl1' | 'lvl2' | 'lvl3' | 'lvl4' | 'lvl5' | 'lvl6' ;
anchor : string | null ;
hierarchy : {
lvl0 : string ;
lvl1 : string ;
lvl2 : string | null ;
lvl3 : string | null ;
lvl4 : string | null ;
lvl5 : string | null ;
lvl6 : string | null ;
};
_highlightResult : DocSearchHitHighlightResult ;
_snippetResult : DocSearchHitSnippetResult ;
// ... additional optional fields
};
Working with Highlight and Snippet Results
_highlightResult and _snippetResult contain HTML-formatted text with search term matches wrapped in highlight tags:
< DocSearch
appId = "YOUR_APP_ID"
apiKey = "YOUR_SEARCH_API_KEY"
indexName = "documentation"
transformItems = { ( items ) => {
return items . map (( item ) => {
// Access highlighted content
const highlightedTitle = item . _highlightResult ?. hierarchy ?. lvl1 ?. value || item . hierarchy . lvl1 ;
const snippetContent = item . _snippetResult ?. content ?. value || item . content ;
return {
... item ,
// You can use these in custom hit components
highlightedTitle ,
snippetContent ,
};
});
} }
/>
Combining with Other Features
With Multiple Indices
transformItems processes hits from all indices together:
< DocSearch
appId = "YOUR_APP_ID"
apiKey = "YOUR_SEARCH_API_KEY"
indices = { [
{ name: 'docs_v1' , searchParameters: { facetFilters: [ 'version:v1' ] } },
{ name: 'docs_v2' , searchParameters: { facetFilters: [ 'version:v2' ] } },
] }
transformItems = { ( items ) => {
// All hits from both indices
return items . map (( item ) => {
const indexName = item . __autocomplete_indexName ;
const version = indexName . includes ( 'v2' ) ? 'v2' : 'v1' ;
return {
... item ,
versionBadge: version ,
};
});
} }
/>
With Custom Hit Component
Pass transformed data to your custom component:
function CustomHit ({ hit }) {
return (
< a href = { hit . url } >
< h4 > { hit . hierarchy . lvl1 } </ h4 >
{ hit . isNew && < span className = "badge" > New </ span > }
{ hit . formattedDate && < time > { hit . formattedDate } </ time > }
</ a >
);
}
< DocSearch
appId = "YOUR_APP_ID"
apiKey = "YOUR_SEARCH_API_KEY"
indexName = "documentation"
transformItems = { ( items ) => {
return items . map (( item ) => ({
... item ,
isNew: Date . now () - new Date ( item . date ). getTime () < 7 * 24 * 60 * 60 * 1000 ,
formattedDate: new Date ( item . date ). toLocaleDateString (),
}));
} }
hitComponent = { CustomHit }
/>
transformItems runs on every search query. Avoid expensive operations like API calls or complex computations that could slow down the search experience.
Good Practices
// β
Fast transformations
transformItems = {(items) => {
return items . map (( item ) => ({
... item ,
displayUrl: item . url . replace ( / ^ https ? : \/\/ / , '' ),
}));
}
// β
Simple filtering
transformItems = {(items) =>
items.filter((item) => item. status === 'published' )
}
// β
Basic sorting
transformItems = {(items) =>
items.sort(( a , b) => (b.priority || 0 ) - (a.priority || 0 ))
}
// β API calls
transformItems = { async ( items ) => {
// Don't do this!
return Promise . all (
items . map ( async ( item ) => {
const data = await fetch ( `/api/enrich/ ${ item . objectID } ` );
return { ... item , ... data };
})
);
}}
// β Heavy computations
transformItems = {(items) => {
return items . map (( item ) => {
// Don't run expensive algorithms here
const score = complexScoringAlgorithm ( item );
return { ... item , score };
});
}}
Debugging Tips
Console Logging Add console.log(items) in your transform function to inspect the raw hits and understand the data structure.
transformItems = {(items) => {
console . log ( 'Raw items:' , items );
const transformed = items . map ( /* ... */ );
console . log ( 'Transformed items:' , transformed );
return transformed ;
}
Type Checking Use TypeScript to ensure your transformed items match the expected structure.
import type { DocSearchHit } from '@docsearch/react' ;
transformItems : ( items : DocSearchHit []) : DocSearchHit [] => {
return items . map (( item ) => ({
... item ,
// TypeScript will validate this structure
}));
}
Complete Example
import { DocSearch } from '@docsearch/react' ;
import '@docsearch/css' ;
function App () {
return (
< DocSearch
appId = "YOUR_APP_ID"
apiKey = "YOUR_SEARCH_API_KEY"
indices = { [
{ name: 'documentation' },
{ name: 'blog_posts' },
] }
transformItems = { ( items ) => {
return items
// Filter out draft content
. filter (( item ) => item . status !== 'draft' )
// Add computed properties
. map (( item ) => {
const indexName = item . __autocomplete_indexName || '' ;
const isDoc = indexName . includes ( 'documentation' );
const isBlog = indexName . includes ( 'blog' );
return {
... item ,
// Add source badge
hierarchy: {
... item . hierarchy ,
lvl0: ` ${ isDoc ? 'π' : isBlog ? 'βοΈ' : 'π' } ${ item . hierarchy . lvl0 } ` ,
},
// Normalize URLs
url: item . url . replace ( / ^ https ? : \/\/ [ ^ / ] + / , '' ),
url_without_anchor: item . url_without_anchor . replace ( / ^ https ? : \/\/ [ ^ / ] + / , '' ),
// Add metadata
sourceType: isDoc ? 'documentation' : isBlog ? 'blog' : 'other' ,
};
})
// Sort by relevance
. sort (( a , b ) => {
// Prioritize documentation over blog
if ( a . sourceType === 'documentation' && b . sourceType !== 'documentation' ) return - 1 ;
if ( a . sourceType !== 'documentation' && b . sourceType === 'documentation' ) return 1 ;
return 0 ;
});
} }
maxResultsPerGroup = { 10 }
/>
);
}