This page holds an assortment of examples of using Search Element callbacks. They supplement the examples found in [Callbacks section](https://developers.google.com/custom-search/docs/element#search-time-callbacks) of the Custom Search Element API document.

> [!NOTE]
> **Note:** Executable examples on this page run on the [JSFiddle](https://jsfiddle.net) web site. Look for the jsfiddle button, ![JSFiddle icon](https://developers.google.com/custom-search/images/jsfiddle-icon.png), at the upper right of the code block. Pressing it will open a JSFiddle page with the example displayed in `HTML`, `JavaScript`, `css`, and *run* panes. It starts "alive", so just enter a query in the searchbox shown in the run pane and the callback runs.
>
>
> Examples in this document are divided into parts according to their type and function, but:
>
> - All the parts of an example are sent to JSFiddle together
> - Each part of the example has a JSFiddle button. They all do the same thing.
>
>
> Since this is JSFiddle, you can play with the code. Changes made in JSFiddle are not propagated
> back to this document.

## Search Starting Callback Examples


The *search starting* callback can modify the query before it is used for the search.
The Programmable Search Engine can be configured to include pre-determined terms in the query, but
this callback can modify the query based on any information available to the callback
function.


The following *search starting* callback decorates each query with the current day
of week.
**Search Starting Callback Example**

    <script async
      src="https://cse.google.com/cse.js?cx=000888210889775888983:g9ckaktfipe"></script>

    const mySearchStartingCallback = (gname, query) => {
      const dayOfWeek = new Date().getDay();
      console.log(dayOfWeek);
      var days = {
            "0": "Sunday",
            "1": "Monday",
            "2": "Tuesday",
            "3": "Wednesday",
            "4": "Thursday",
            "5": "Friday",
            "6": "Saturday"
        };

        return query + ' ' + days[dayOfWeek];
    };
    // Install the callback.
    window.__gcse || (window.__gcse = {});
      window.__gcse.searchCallbacks = {
        image: {
          starting: mySearchStartingCallback,
        },
        web: {
          starting: mySearchStartingCallback,
        },
    };


Include these elements in the HTML:

    <div class="gcse-searchbox"></div>
    <div class="gcse-searchresults"></div>

**Displaying "Recent Searches" Links**


This example demonstrates how to implement a **Recent Searches** feature using the
*search starting* callback.


The feature captures user queries when a search is triggered and stores them locally in the
browser. These queries are then displayed as clickable items, allowing users to quickly
repeat previous searches, improving usability and reducing repeated typing.

> [!CAUTION]
> **Important:** Under data privacy regulations (such as the EU ePrivacy Directive and GDPR), storing or retrieving information on a user's device using client-side storage like `localStorage` typically requires prior, informed user consent unless strictly necessary to deliver a service explicitly requested by the user. Because search history may constitute personal data and is not strictly necessary for basic site navigation, ensure your implementation complies with applicable privacy laws by obtaining explicit user consent before persisting search queries.

    <script async
      src="https://cse.google.com/cse.js?cx=000888210889775888983:g9ckaktfipe"></script>


Define the helper function to store recent searches in `localStorage`, guarded by
a user consent check:

          // For this demo, consent is assumed to be granted.
            // In production, integrate this with your site's Consent Management Platform (CMP).
            window.__hasStorageConsent = true;

            function hasStorageConsent() {
              return window.__hasStorageConsent === true;
            }

            function saveRecentSearch(query) {
              if (!query || !hasStorageConsent()) return;

              let searches = [];
              try {
                searches = JSON.parse(localStorage.getItem("recentSearches")) || [];
              } catch (e) {
                console.warn("Invalid localStorage data, resetting:", e);
                // searches remains [] to overwrite the corrupted data
              }

              searches = searches.filter(item => item !== query);
              searches.unshift(query);
              searches = searches.slice(0, 5);

              try {
                localStorage.setItem("recentSearches", JSON.stringify(searches));
              } catch (e) {
                console.warn("Unable to access localStorage:", e);
              }
            }
          
        

Register the *search starting* callback. Callbacks must be registered in the global
`__gcse` object before loading `cse.js`:

          // Install the callback.
            window.__gcse || (window.__gcse = {});
            window.__gcse.searchCallbacks = {
              web: {
                starting: function (gname, query) {
                  saveRecentSearch(query);
                  renderRecentSearches();
                  return query; // continue normal search
                },
              },
            };
          
        

Define a function to render recent searches as clickable items and initialize it on page load:

          function renderRecentSearches() {
            const container = document.getElementById("recent-searches");
            if (!container) return;
            if (!hasStorageConsent()) {
              container.innerHTML = "";
              try {
                localStorage.removeItem("recentSearches");
              } catch(e) {}
              return;
            }
            let searches = [];
            try {
              searches = JSON.parse(localStorage.getItem("recentSearches")) || [];
            } catch (e) {
              console.warn("Unable to access localStorage:", e);
            }
            container.innerHTML = "";
            searches.forEach(q => {
              const item = document.createElement("button");
              item.type = "button";
              item.textContent = q;
              item.onclick = () => {
                const input = document.querySelector("input.gsc-input");
                const button = document.querySelector("button.gsc-search-button");
                if (input) input.value = q;
                if (button) button.click();
              };
              container.appendChild(item);
            });
          }

          window.addEventListener("DOMContentLoaded", function () {
            renderRecentSearches();
          });
          
        

Include these elements in the HTML:

          
            <style>
              #recent-searches button {
                display: inline-block;
                margin-right: 8px;
                margin-top: 4px;
                padding: 2px 8px;
                background-color: #f1f3f4;
                border: 1px solid #dadce0;
                border-radius: 4px;
                cursor: pointer;
                font-size: 13px;
              }
            </style>
            <div class="gcse-search"></div>
            <div id="recent-searches"></div>
          
        
#### Limitations

- Data is stored only in the browser (`localStorage`) and requires user consent.
- Not shared across devices or users.
- Cleared if browser storage is cleared.
- Does not track searches made outside this page.

## Results Rendered Callback Examples


The results rendered callback is good for modifying the page after it's populated with results.
It is designed to make it easy to modify the display of results without requiring the callback
to take full responsibility for rendering the results.


The following examples illustrate two applications of the results rendered callback that
do not operate on the results.
**Identify Last Results Page**


This *results rendered* callback notices that we're displaying the last page
of results, and pops up an alert reminding the user that they've reached the end.

    <script async
      src="https://cse.google.com/cse.js?cx=000888210889775888983:y9tkcjel090"></script>

    myWebResultsRenderedCallback = function(){
        var searchresults= document.getElementsByClassName("gsc-cursor-page");
        var index= document.getElementsByClassName("gsc-cursor-current-page");
        if(index.item(0).innerHTML == searchresults.length){
           alert("This is the last results page");
        }
    };


Install the callback

    window.__gcse || (window.__gcse = {});
    window.__gcse.searchCallbacks = {
      web: {
          // Since the callback is in the global namespace, we can refer to it by name,
          // 'myWebResultsRenderedCallback', or by reference, myWebResultsRenderedCallback.
          rendered: myWebResultsRenderedCallback,
      },
    };


Include these elements in the HTML:

    <div class="gcse-searchbox"></div>
    <div class="gcse-searchresults"></div>

**Increasing the font size of the "cursor" links**


This *results rendered* callback demo increases the font size of the
"cursor" numbers that select pages of results.


The default font size is 12px. Here, we increase it to 20px.

    <script async
      src="https://cse.google.com/cse.js?cx=000888210889775888983:y9tkcjel090"></script>

    myWebResultsRenderedCallback = function(){
       document.getElementsByClassName("gsc-cursor")[0].style.fontSize = '20px';
    };


Install the callback

    window.__gcse || (window.__gcse = {});
    window.__gcse.searchCallbacks = {
      web: {
          // Since the callback is in the global namespace, we can refer to it by name,
          // 'myWebResultsRenderedCallback', or by reference, myWebResultsRenderedCallback.
          rendered: myWebResultsRenderedCallback,
      },
    };


Include these elements in the HTML:

    <div class="gcse-searchbox"></div>
    <div class="gcse-searchresults"></div>

**Use Letters for "Cursor" Labels**


This *results rendered* callback changes the page links in the "cursor"
from numbers to letters.

    <script async
      src="https://cse.google.com/cse.js?cx=000888210889775888983:y9tkcjel090"></script>

    myWebResultsRenderedCallback = function(){
        var arr = document.getElementsByClassName('gsc-cursor-page');
        var alp = ['A','B','C','D','E','F','G','H','I','J','K','L',
          'M','N','O','p','Q','R','S','T','U','V','W','X','Y','Z'];
        for (var i = 0; i &lt arr.length; i++) {
            arr[i].innerHTML = alp[i];
        }
    };


Install the callback

    window.__gcse || (window.__gcse = {});
    window.__gcse.searchCallbacks = {
      web: {
          // Since the callback is in the global namespace, we can refer to it by name,
          // 'myWebResultsRenderedCallback', or by reference, myWebResultsRenderedCallback.
          rendered: myWebResultsRenderedCallback,
      },
    };


Include these elements in the HTML:

    <div class="gcse-searchbox"></div>
    <div class="gcse-searchresults"></div>

## Results Ready Callback Examples

**Display Results with Alternating-Color Backgrounds**


This callback formats results with alternating light and dark backgrounds.

    <script async
          src="https://cse.google.com/cse.js?cx=000888210889775888983:y9tkcjel090"></script>


Note: This code is written in JavaScript/ES6. It will run on most browsers, but will need
to converted to JavaScript/ES5 for Internet Explorer and a few other older
browsers.

    barredResultsRenderedCallback = function(gname, query, promoElts, resultElts){
      const colors = ['Gainsboro', 'FloralWhite'];
      let colorSelector = 0;
      for (const result of resultElts) {
        result.style.backgroundColor = colors[colorSelector];
        colorSelector = (colorSelector + 1) % colors.length;
      }
    };
    window.__gcse || (window.__gcse = {});
    window.__gcse.searchCallbacks = {
      web: {
        rendered: barredResultsRenderedCallback,
      },
    };


Include these elements in the HTML:

    <div class="gcse-searchbox"></div>
    <div class="gcse-searchresults"></div>

### Word Cloud


The obvious application of the *results ready* callback is to display search results in
a format that would be hard to reach using the *results rendered* callback to tweak the
HTML. The *results ready* callback starts with an empty `div`.
[One example](https://developers.google.com/custom-search/docs/element#results-ready) in the *Search Element API* document
showed how use the callback to render a very simple version of results.
[Another example](https://developers.google.com/custom-search/docs/element#results-ready-and-rendered)
showed how to hold the results data from the *results ready* callback and pass it to
the *results rendered* callback where it can be used to decorate the standard results
display.


The following *results ready* callback shows that search results do not have to
be a list of results. It replaces the normal display of search results
with a word cloud of the words found in the results' titles and content. When the list of
results is only an intermediate step for your user, a callback like this can bypass that stage
and use the results to present the report the user wants.
**Create a Word Cloud from Search Results Content**

    <script async
          src="https://cse.google.com/cse.js?cx=000888210889775888983:y9tkcjel090"></script>
    <style>
      #container {
        width: 100%;
        height: 4.5in;
        margin: 0;
        padding: 0;
      }
    </style>
    <script src="https://cdn.anychart.com/releases/v8/js/anychart-base.min.js"></script>
    <script src="https://cdn.anychart.com/releases/v8/js/anychart-tag-cloud.min.js"></script>


Note: This code is written in JavaScript/ES6. It will run on most browsers, but will need
to be converted to JavaScript/ES5 for Internet Explorer and a few other
older browsers.

    const resultsReadyWordCloudCallback = function(
            name, q, promos, results, resultsDiv) {
        const stopWords = new Set()
          .add('a')
          .add('A')
          .add('an')
          .add('An')
          .add('and')
          .add('And')
          .add('the')
          .add('The');

        const words = {};
        const splitter = /[""",\?\s\.\[\]\{\};:\-\(\)\/!@#\$%\^&*=\+\*]+/;
        if (results) {
            for (const {contentNoFormatting, titleNoFormatting} of results) {
                const wordArray = (contentNoFormatting + ' ' + titleNoFormatting)
                  .split(splitter)
                  .map(w => w.toLowerCase());
                for (const w of wordArray) {
                    if (w && !stopWords.has(w)) {
                        words[w] = (words[w] + 1) || 1;
                    }
                }
            }
        }
        const dataForChart = [];
        for (const key in words) {
            const val = words[key];
            dataForChart.push({
                'x': key,
                'value': val,
            });
        }

        const container = document.createElement('div');
        resultsDiv.appendChild(container);
        container.id = 'container';
        // create a tag (word) cloud chart
        const chart = anychart.tagCloud(dataForChart);
        // set a chart title
        chart.title(`Words for query: "${q}"`)
        // set an array of angles at which the words will be laid out
        chart.angles([0, 45, 90, 135])
        // display the word cloud chart
        chart.container(container);
        chart.draw();
        return true; // Don't display normal search results.
    };
    window.__gcse || (window.__gcse = {});
    window.__gcse.searchCallbacks = {
        web: {
            ready: resultsReadyWordCloudCallback,
        },
    };


Include these elements in the HTML:

    <div class="gcse-searchbox"></div>
    <div class="gcse-searchresults"></div>

## Two Part Callback Example


The *results ready* and *results rendered* callbacks can be used in combination to pass information from the former to the latter.
For example, the information in the array of result objects is available to the *results ready* callback, but not *results rendered* callback.
By saving that information to an array as part of the *results ready* callback we can make it accessible to the *results rendered* callback.


One example of this is to bypass the preview panel shown when an image result is clicked.
With a two-part callback we can have the image results link directly to the corresponding websites rather than display an image preview when clicked.
**Bypass image previews**

    <script async
      src="https://cse.google.com/cse.js?cx=000888210889775888983:g9ckaktfipe"></script>

    const makeTwoPartCallback = () => {
      let urls;
      const readyCallback = (name, q, promos, results, resultsDiv) => {
        urls = [];
        for (const result of results) {
          urls.push(result['contextUrl']);
        }
      };
      const renderedCallback = (name, q, promos, results) => {
        const removeEventListeners = element => {
          const clone = element.cloneNode(true);
          element.parentNode.replaceChild(clone, element);
          return clone;
        };
        for (let i = 0; i < results.length; ++i) {
          const element = removeEventListeners(results[i]);
          element.addEventListener('click', () => window.location.href = urls[i]);
        }
      };
      return {readyCallback, renderedCallback};
    };
    const {
      readyCallback: imageResultsReadyCallback,
      renderedCallback: imageResultsRenderedCallback,
    } = makeTwoPartCallback();
    window.__gcse || (window.__gcse = {});
    window.__gcse.searchCallbacks = {
      image: {
        ready: imageResultsReadyCallback,
        rendered: imageResultsRenderedCallback,
      },
    };


Include these elements in the HTML:

    <div class="gcse-searchbox"></div>
    <div class="gcse-searchresults"></div>