Learn about connectors in InstantSearch, how they separate business logic from rendering, and when to use them for custom widgets.
Connectors are functions that encapsulate the business logic of widgets, separating it from the rendering layer. This makes the same search logic reusable across different frameworks and rendering approaches.
The second argument is a boolean indicating if this is the initial render:
function render(renderOptions, isFirstRender) { if (isFirstRender) { // Create DOM elements, set up event listeners } else { // Update existing elements with new data }}
Here’s a complete example using connectSearchBox (based on /home/daytona/workspace/source/packages/instantsearch.js/src/connectors/search-box/connectSearchBox.ts):
import { connectSearchBox } from 'instantsearch.js/es/connectors';const customSearchBox = connectSearchBox( (renderOptions, isFirstRender) => { const { query, refine, clear, widgetParams } = renderOptions; const { container } = widgetParams; if (isFirstRender) { // Create the HTML structure const input = document.createElement('input'); const button = document.createElement('button'); input.placeholder = 'Search...'; button.textContent = 'Clear'; // Set up event listeners input.addEventListener('input', (e) => { refine(e.target.value); }); button.addEventListener('click', () => { clear(); input.value = ''; }); container.appendChild(input); container.appendChild(button); } // Update the input value const input = container.querySelector('input'); input.value = query; }, // Optional unmount function () => { const container = document.querySelector('#searchbox'); container.innerHTML = ''; });// Use the custom widgetsearch.addWidgets([ customSearchBox({ container: document.querySelector('#searchbox'), })]);
The SearchBox connector provides these render options (from /home/daytona/workspace/source/packages/instantsearch.js/src/connectors/search-box/connectSearchBox.ts:34-54):
type SearchBoxRenderState = { query: string; // Current search query refine: (value: string) => void; // Function to update query clear: () => void; // Function to clear query isSearchStalled: boolean; // Whether search is stalled};
Example implementation:
const { query, refine, clear } = renderOptions;// Display current queryconsole.log('Current query:', query);// Update the queryrefine('new search term');// Clear the searchclear();
Clean up in the unmount function: Always provide an unmount function to remove event listeners and clean up DOM elements.
connectSearchBox( renderFn, () => { // Remove event listeners // Clear DOM elements })
Use isFirstRender efficiently: Only create DOM elements and set up event listeners on the first render. Update existing elements on subsequent renders.
Type your connectors: In TypeScript, use generic types for better type safety:
type ProductHit = { name: string; price: number;};const customHits = connectHits<ProductHit>( ({ items }) => { // items is typed as Array<Hit<ProductHit>> });