Quick Tip: How to Style Google Custom Search Manually

Almir Bijedic

This article was peer reviewed by Mark Brown. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

Website owners very often decide on using Google’s Custom Search Engine (GCSE) for searching through their content instead of using built-in and/or custom search functionality. The reason is simple – it’s much less work, and most often it does the trick. If you don’t need advanced filters or custom search parameters then GSCE is for you.

In this quick tip, I will show you how to manually render the search form (without using a special GCSE tag) and a results box which allows for more control and a cleaner way to style the search input field.

The Problem

Usually adding GCSE to your site is as simple as copy pasting a script and a custom HTML tag into your site. In the place where you put the special GCSE tag, an input search field will render. Typing and starting a search from this field will do a Google search based on previously configured parameters (e.g. search sitepoint.com only).

One question that often comes up is “How do I change the placeholder of the GCSE input field?”. Unfortunately very often the suggested answer is wrong as it uses an unreliable setTimeout method in order to wait for the Ajax call from the GCSE to complete (making sure the input is already attached to the DOM) and then changes attributes via JavaScript.

We are also going to query the element and change the attributes with JS, but instead of doing a blind setTimeout() we will use the callback provided by the GCSE which will guarantee that the input has already loaded.

Creating a GCSE Account

The search engine is configured completely online. The first step is to go to the GCSE site and click add. Follow the wizard by filling in the domain on which you want to search (usually your site URL). You can ignore any advanced settings for now.

When you click done, you are presented with three options:

  1. Get Code, which will guide you through what and where you have to copy in order for the search to show on your site
  2. Public URL will show you a working preview of the search that you set up
  3. Control Panel for customising the search

Go to Control Panel, click Search Engine ID and make a note of this value for later.

HTML Setup

In order to try this out, we will create a basic index.html containing the HTML needed and an app.js file containing the functions needed to render and customise the search.

Go ahead and create a basic HTML file with this inside:

<!DOCTYPE html>
<html lang="en">
    <meta charset="UTF-8">
    <h1>GCSE test</h1>
    <div class="gcse-search-wrapper"></div>
    <div class="gcse-results-wrapper"></div>
    <script src="app.js"></script>

We added two <div>‘s with special classes in order to be recognized as the elements where the search form and results should be rendered.

Functions for Manual Rendering

Now go into your app.js file and add this:

var config = {
  gcseId: '006267341911716099344:r_iziouh0nw',
  resultsUrl: 'http://localhost:8080',
  searchWrapperClass: 'gcse-search-wrapper',
  resultsWrapperClass: 'gcse-results-wrapper'

var renderSearchForms = function () {
  if (document.readyState == 'complete') {
  } else {
    google.setOnLoadCallback(function () {
    }, true);

var queryAndRender = function() {
  var gsceSearchForms = document.querySelectorAll('.' + config.searchWrapperClass);
  var gsceResults = document.querySelectorAll('.' + config.resultsWrapperClass);

  if (gsceSearchForms) {
  if (gsceResults) {

var renderSearch = function (div) {
        div: div.id,
        tag: 'searchbox-only',
        attributes: {
          resultsUrl: config.resultsUrl
    if (div.dataset &&
        div.dataset.stylingFunction &&
        window[div.dataset.stylingFunction] &&
        typeof window[div.dataset.stylingFunction] === 'function') {

var renderResults = function(div) {
      div: div.id,
      tag: 'searchresults-only'

window.__gcse = {
  parsetags: 'explicit',
  callback: renderSearchForms

(function () {
  var cx = config.gcseId;
  var gcse = document.createElement('script');
  gcse.type = 'text/javascript';
  gcse.async = true;
  gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') +
    '//cse.google.com/cse.js?cx=' + cx;
  var s = document.getElementsByTagName('script')[0];
  s.parentNode.insertBefore(gcse, s);

First, we declare some variables for the configuration. Put the ID you noted down earlier into the gcseId field of the config. Put the URL of your local index.html file into the resultsUrl field. This is where the search will redirect after a user submits a query. Additionally, GCSE will expect there to be a results field rendered on the provided URL.


This function checks if the page has been loaded and, if so, calls the function that will take care of rendering queryAndRender() or, if the document is not yet loaded, then set a callback in order to return here later after the document has finished loading.


This function queries the DOM for elements with the classes provided in the config. If a wrapper div is found, call renderSearch() and renderResults() for rendering the search and results fields respectively.


Here is where the actual magic happens.

We use the Google Search API (more documentation on how to use the google.search.cse.element object here) to create the search box, and if there is a query active (results), the results box.

The render function takes more arguments than are provided in this example, so be sure to check the documentation if further customization is needed. The div argument actually takes the ID of the div where we want the search rendered, and the tag argument is to denote what exactly do we want to render (results or search or both).

Additionally, renderSearch() looks for the data attributes of the wrapper element, and if there is a styling-function attribute given, it will look for that function name in the scope and apply it on the element. This is our opportunity where we can style the element.

window.__gcse = {
  parsetags: 'explicit',
  callback: renderSearchForms

In this code snippet, we set a callback variable in the global scope, so that GCSE uses this internally and executes the callback function when it has finished loading. This is the part which makes this method so much better than the setTimeout() solution for editing the placeholder (or anything else) of the input field.

Test Run

So far, we have included everything needed to render a search box and the results. If you have node.js installed, go into the folder where you placed the index.html and app.js files, and run the http-server command. Which will server the content from the folder on localhost on port 8080 by default.

Test Run

Styling Function

Now we are ready to add the custom styling function to the search div. Go back to index.html and on the #searchForm div, add a styling-function attribute

<div styling-function="removePlaceholder"
id="searchForm" class="gcse-search-wrapper"></div>

Now go into app.js and on top of the file, under the config variable declaration, add a new function:

var removePlaceholder = function(div) {
  var inputField = div.querySelector("input.gsc-input");

  // Change the placeholder
  inputField.placeholder = "Search SitePoint";

  // Remove the background
  inputField.style.background = "none";

  // The background will get re-attached on blur, so add an event that will also remove it on blur
  // Another way to do this would be to de-attach the element from the DOM and then re-attach again, hence wiping the listeners
  inputField.addEventListener("blur", function() {
    inputField.style.background = "none";

Now try to load the test page again, and you will see a proper placeholder.



For setting up a simple search quickly, especially if the site is just static HTML, the Google Custom Search Engine works perfectly. With a little bit of JavaScript it’s possible to customize not only the search form, but also the results page, providing a more seamless experience for users.

Are you using GCSE, or have you found a better solution? Comment below!