Integrating Search Expander

Integrating Search Expander into your search results page consists of the following steps:

  • Creating a search engine project within your Search Expander account
  • Adding some markup to your search engine results page
  • Loading the Search Expander JavaScript library on the page
  • Calling the global sxpr() JavaScript function after your search results have loaded

The sxpr() function will call the Search Expander API, fetch widget data, and render it in the HTML containers you have specified. It must be passed an object with an se property whose value is your search engine project ID. Widget behaviour and appearance can be customised by adding additional properties to this object.

You can include the Search Expander JavaScript library using:

<script src="https://cdn.searchexpander.com/js/sxpr.js"></script>

To run Search Expander, call sxpr() after the script has loaded:

sxpr({
    se: 'your-search-engine-id', // Replace 'your-search-engine-id' with your search engine's ID
    // Add other Search Expander settings here...
});

To create the appropriate widgets, Search Expander needs the following information about the page:

  1. Where to render the widgets
  2. The user's search query
  3. The search query's results
  4. How to transform a search query into a SERP request

Although Search Expander will try to guess 2-4, it is recommended to specify them explicitly. This is easy to accomplish via some simple changes to your markup or by adding properties to the object passed to sxpr().

Widget containers

Search Expander widgets will render inside a number of HTML container elements:

  1. Knowledge panel
  2. Instant answers
  3. Media thumbnail bar
  4. Top bar

Container placement

Typically, the widget containers will be placed in the page locations described below. However, they can be placed anywhere on the page and their contents will adjust responsively.

  1. The knowledge panel will be a sidebar next to the mainline search results.
  2. The instant answers widget will be above the mainline search results.
  3. The media thumbnail bar will run across the top of the search results.
  4. The top bar will run across the top of the search results, above the media thumbnail bar.

You can identify widget containers in your markup using data-sxpr-... attributes, or reference them directly on the settings object passed to sxpr():

Widget HTML attribute Settings property
Knowledge panel data-sxpr-knowledge-panel knowledgePanelContainer
Instant answers data-sxpr-instant-answers instantAnswersContainer
Media thumbnail bar data-sxpr-media-thumb-bar mediaThumbBarContainer
Top bar data-sxpr-top-bar topBarContainer

If you are serving product ads, you can also provide a container for shopping results, which will typically be on a separate page from the web SERP:

Widget HTML attribute Settings property
Shopping data-sxpr-shopping shoppingContainer
Important: If you are serving product ads, please check our list of supported markets.

Markup example

<div data-sxpr-top-bar>
    <!-- Top bar, including search suggestions, will render here -->
</div>
<div data-sxpr-media-thumb-bar>
    <!-- Media thumbnail bar will render here -->
</div>
<div id="main-content">
    <div id="mainline">
        <div data-sxpr-instant-answers>
            <!-- Instant answers will render here -->
        </div>
        <div>
            <!-- ... your search results here ... -->
        </div>
    </div>
    <div id="sidebar">
        <div data-sxpr-knowledge-panel>
            <!-- Knowledge panel will render here -->
        </div>
    </div>
</div>

JavaScript example

sxpr({
    se: 'your-search-engine-id',
    knowledgePanelContainer: document.querySelector('#my-kp-container'), // or pass a selector string
    mediaThumbBarContainer: document.querySelector('#my-mtb-container'),
    instantAnswersContainer: document.querySelector('#my-ia-container'),
    topBarContainer: document.querySelector('#my-top-bar-container'),
    // ... other settings
});

Search query

Add the data-sxpr-input attribute to a search input, and Search Expander will read its value when sxpr() is called:

<input name="q" value="lorem ipsum" data-sxpr-input>

Alternatively, you can specify the current search query directly using the q property of the sxpr() settings argument:

sxpr({
    se: 'your-search-engine-id',
    q: 'lorem ipsum',
});

Or you can specify a search input using the searchInput property:

sxpr({
    se: 'your-search-engine-id',
    searchInput: document.querySelector('input[name="q"]'), // or pass a selector string
});

Search results

Search Expander needs to know the page's current search results. The simplest way of allowing this is to add a data-sxpr-link attribute to each of your search result links. Alternatively, you can pass your results directly to sxpr() in the form of an array of { url: string; title: string } objects.

In the absence of the above, Search Expander will make a guess about where your page's search results are, but it is more reliable to make things explicit.

Some markup examples:

<a data-sxpr-link href="https://example.com/">Example Page Title</a>
<div data-sxpr-link>
    <a href="https://example.com/">Example Page Title</a>
    ...
</div>
<div data-sxpr-link="https://example.com/" data-sxpr-title="Example Page Title">
    <!-- You can specify url and title as attribute values on any elements -->
    ...
</div>

Or for a direct approach:

sxpr({
    se: 'your-search-engine-id',
    results: [
        { 
            url: "https://example.com/",
            title: "Example Page Title"
        },
        { 
            url: "https://example.com/example2",
            title: "Another Example"
        },
        // ...
    ],
    // ... other settings
});
Note: Search APIs will sometimes return display-friendly URLs to be rendered on the page, in addition to the complete URLs that are used in search result links. Search Expander needs the complete URLs, because display-friendly URLs may be truncated and fail to load properly.

There may be cases where you cannot modify the HTML of your search result links or where the search results cannot be known at the time of page load. See Dynamic Search Results for how to deal with these cases.

Search requests

Search Expander widgets contain links for new user searches. In order to construct the link URLs, Search Expander needs a URL template. For example:

https://example.com/search?q={searchTerms}

…replacing example.com with your domain.

Relative URLs with absolute paths are allowed, for example:

/search?q={searchTerms}

This URL template should be added as searchUrlTemplate on the settings object passed to sxpr().

There are some situations where this approach may be inadequate. For example, where:

  • Search requests are initiated by JavaScript code rather than standard page navigation
  • Search requests are POST requests rather than GET requests
  • Additional URL parameters need to be set dynamically for search requests

You can deal with these requirements by setting a handleSearchClick function on the sxpr() settings object. In this case, when a widget search link is clicked, the link's default behaviour will be overridden and the function will be left to handle the search request. handleSearchClick is passed an object with the following interface, to provide the information with which a search query can be created:

{
    q: string;
    mediaThumbBar: string | undefined;
    searchSuggestions: string | undefined;
    wordToDefine: string | undefined;
    link: HTMLAnchorElement;
    event: MouseEvent;
    url: string | undefined; // If searchUrlTemplate is set, this is the search URL that would be followed by default
}

More details with examples can be found here.

Additional notes

By default, searchUrlTemplate will have a query string automatically added to it containing values for mediaThumbBar, searchSuggestions and wordToDefine. These values trigger extra widget content. In order to control how these appear in the URL, you can add these tags yourself, for example:

https://example.com/search?q={searchTerms}&customkey1={mediaThumbBar}&customkey2={searchSuggestions}&customkey3={wordToDefine}

Dynamic search results

There are situations in which search results may be loaded via JavaScript after the rest of the page, or where you cannot edit the search results markup. For example, the results may be provided by a Google Programmable Search Engine. In these cases, the call to sxpr() must be deferred until after the results have been rendered. You can handle this by calling sxpr() inside a callback function or after an await expression. Here's a simplified example:

async function main() {
    // No search results on the page yet...

    // Get the user search query
    const q = document.querySelector('#search-input').value;

    // Fetch results based on the search query
    const results = await fetchSearchResults(q);
    
    // Render the results on the page
    renderSearchResults(results);

    // Now it's safe to run Search Expander:
    sxpr({
        se: 'your-search-engine-id',
        q,
        results,
        // ... other settings
    });
}

// Fetches search results data from an external API, and returns an array of
// { url: string, title: string } objects
async function fetchSearchResults(q) {
    // ...
}

// Renders the search results on the page
function renderSearchResults(results) {
    // ...
}

main();

Below is an example of calling sxpr() in a Google Programmable Search Engine (v2) callback:

window.__gcse = {
    searchCallbacks: {
        web: {
            rendered: () => {
                sxpr({
                    se: 'your-search-engine-id',
                    // ... other settings
                });
            }
        }
    }
};

Basic example

The example below assumes that the search results are rendered directly onto the page, before any JavaScript is executed.

...
<main>
    <h1>Search Page</h1>
    
    <form>
        <input data-sxpr-input name="q" type="search" placeholder="Search">
        <button type="submit">Search</button>
    </form>
    
    <div data-sxpr-top-bar>
        <!-- Top bar, including search suggestions, will render here -->
    </div>
    
    <div data-sxpr-media-thumb-bar>
        <!-- Media thumbnail bar will render here -->
    </div>
    
    <div id="content">
        <div id="mainline">
            <div data-sxpr-instant-answers>
                <!-- Instant answers will render here -->
            </div>
            <div id="results">
                <div class="result">
                    <a data-sxpr-link href="https://example.com/">Example Page</a>
                    <div class="url">https://example.com/</div>
                </div>
                <div class="result">
                    <a data-sxpr-link href="https://example.com/example2">Another Example Page</a>
                    <div class="url">https://example.com/example2</div>
                </div>
                <!-- ... more search results here -->
            </div>
        </div>
        <div id="sidebar">
            <div data-sxpr-knowledge-panel>
                <!-- Knowledge panel will render here -->
            </div>
        </div>
    </div>
</main>

<script src="https://cdn.searchexpander.com/js/sxpr.js"></script>
<script>
sxpr({
    se: 'your-search-engine-id',
    searchUrlTemplate: '/?q={searchTerms}',
});
</script>
...

Multilanguage Support

To set a language for Search Expander results, you can set the lang property on the settings object passed to sxpr(). This property must be a two-letter ISO 639-1 language code. The currently supported languages are:

Language lang value
English (default)en
Deutschde
Españoles
Françaisfr
Italianoit
Nederlandsnl

If you want to allow your users to select their own language, you will need to read this value from somewhere and pass it into the sxpr() function. For example, you could read it from an input:

sxpr({
    se: 'your-search-engine-id',
    q,
    results,
    lang: document.querySelector('[name="language"]').value,
    // ... other settings
});

Or from a URL query string:

sxpr({
    se: 'your-search-engine-id',
    q,
    results,
    lang: new URL(location.href).searchParams.get('lang'),
    // ... other settings
});

If you're loading search results into your page dynamically, you could read a language value from a <select> element before calling sxpr():

<!-- Highly simplified example code! -->

<input type="search" id="search-input" />

<select id="language-selector">
    <option value="en">English</option>
    <option value="de">Deutsch</option>
    <option value="es">Español</option>
    <option value="fr">Français</option>
    <option value="it">Italiano</option>
    <option value="nl">Nederlands</option>
</select>

<script src="https://cdn.searchexpander.com/js/sxpr.js"></script>
<script>
async function main() {
    // Get the user search query
    const q = document.querySelector('#search-input').value;

    // Fetch results based on the search query
    const results = await fetchSearchResults(q);
    
    // Render the results on the page
    renderSearchResults(results);

    // Run Search Expander
    sxpr({
        se: 'your-search-engine-id',
        q,
        results,
        lang: document.querySelector('#language-selector').value,
        // ... other settings
    });
}

// Fetches search results from an API, and returns an array of
// { url: string, title: string } objects
async function fetchSearchResults(q) {
    // ...
}

// Renders the search results on the page
function renderSearchResults(results) {
    // ...
}

main();

</script>

YouTube search result thumbnails

If you wish, you can add thumbnail images for YouTube links into your search engine results by setting youTubeResultThumbs to true in the settings object passed to sxpr(), and indicating where you would like the thumbnails to render by adding the attribute data-sxpr-result-thumb to image containers in your markup. The value of this attribute must be the result URL. For example:

<div id="results">
    <div data-sxpr-result class="result">
        
        <!-- Thumbnail image will be added into the div below if it's a YouTube video URL -->
        <div data-sxpr-result-thumb="https://example.com/" class="result-thumb"></div>
        
        <div class="result-text">
            <a data-sxpr-link href="https://example.com/">Example Page</a>
            <div class="url">https://example.com/</div>
        </div>

    </div>
    
    <!-- ... more search results here -->
</div>

<script src="https://cdn.searchexpander.com/js/sxpr.js"></script>
<script>
sxpr({
    se: 'your-search-engine-id',
    youTubeResultThumbs: true,
    // ... other settings
});
</script>

The data-sxpr-result-thumb elements will have their inner HTML deleted and replaced, so they should be empty.

The thumbnail images come with some default styling, but you may need to update your CSS to make them fit nicely into the surrounding layout.

Note that if the data-sxpr-result-thumb element has an ancestor with a data-sxpr-result attribute, then this ancestor element will be automatically given the class sx-thumb-result, which may help with styling in cases where you want to distinguish result containers with thumbnails from those without.

By default, these images are served directly from YouTube's servers. If you want to proxy the images, add an appropriate value for the imageUrlTemplate setting to the settings object passed to sxpr(). See the page on Private Search Engines for more information.

Google Custom Search Engines

If you are using a Google Custom Search Engine for your results, you may want to set youTubeCSEThumbs to true in the settings object passed to sxpr(). This will attempt to insert YouTube thumbnail images into your CSE result containers. No additional markup is required in this case.

Detecting request completion and errors

You may want to listen for certain Search Expander events while data is fetched and widgets are rendered. To do this, you can add listeners for the following events which are fired on document during the execution of sxpr() and sxpr.shopping():

Event Description
sx-request-aborted Fires when no request should be made (e.g. because of lack of input data)
sx-requesting Fires immediately before a request is made to the Search Expander API
sx-request-complete Fires immediately after a response has been received from the Search Expander API
sx-complete Fires when sxpr() has completed all of its main functions (including rendering widgets)
sx-error Fires when a error is encountered and sxpr() must abort. Information about the error may be found on the detail property of the event.

For example:

document.addEventListener('sx-complete', () => {
    // Do something here when Search Expander completes successfully...
});

Troubleshooting

If Search Expander is not behaving as expected, you can set logLevel on the settings object passed to sxpr() or sxpr.shopping() to get information sent to the browser console log.

logLevel must be set to one of the following values:

  • debug - Logs information about normal operations as well as warnings and errors.
  • warning - Logs information about warnings and errors only.
  • error - Logs information about errors only.

» Shopping Tab

» Styling the Widgets