Drupalwoo ☉

How to add a global search bar to a Drupal site with JavaScript

Visitors should be able to look for content without knowing its URL or where editors placed it. A global search bar gives every page one consistent entry point, while Drupal’s Search module does the matching and returns the results page. JavaScript can add the form to the site header, but it should remain a thin interface over Drupal rather than a separate search system.

This guide shows how to add a global search bar to a Drupal site with JavaScript, attach the script through a small custom module, and keep the search URL correct for multilingual installations. The approach uses plain JavaScript, a standard GET form and the core search route, so it fits custom and contributed themes without a heavyweight front-end framework.

Many Australian publishers serve readers in Sydney, Melbourne, Brisbane and regional areas, often using mobile data during a train or bus journey. A compact site-wide form is useful for a council service, a university course page or a small business looking for opening hours. Australian spelling, local event names and a domain such as example.com.au should all be searchable, and the experience must remain usable on a slow connection.

Choose the right Drupal search scope

Start by deciding what “global” means for the project. Usually it means one search box available throughout the website, not permission to query every application, database or private field on the server. Drupal core Search is a sensible baseline: it indexes node titles, body text and supported attachment text, then applies the access permissions of the visitor who submitted the query.

For a larger publication, search views or an external index may be better. A news site might index event dates and issue numbers, while a council site might need service locations and downloadable documents. Those additions should improve the result set without bypassing Drupal’s access checks.

Before building anything, inspect the content model and ask editors what visitors usually try to find. Search for “school holidays”, “Melbourne Cup” or “opening hours” in a staging site to see whether the wording matches real queries. Field labels and metadata may need to be included in the index, but sensitive fields should stay excluded.

Search is also not a substitute for a sound information architecture. Clear navigation, meaningful page titles and consistent terminology still matter. If people cannot find a page through navigation, adding a wider search scope can conceal that underlying problem rather than solve it.

Prepare the search route and theme region

Enable the Search module at /admin/modules if it is not already active, then visit /search/node?keys=drupal. Check that the route returns the expected results page and that anonymous visitors can search indexed content. Do not assume that a third-party search box automatically respects Drupal roles, unpublished revisions or other content restrictions.

The example obtains its destination from Drupal’s search.page route instead of hard-coding every URL. That matters when a site uses language prefixes, a translated interface or a changed path. The module passes the generated URL to JavaScript through drupalSettings, while the form uses the keys query parameter expected by core.

Decide where the form belongs before changing any code. The header is a familiar location, but it should not crowd the primary menu on a phone. A dedicated region in the theme can make placement predictable, and the script can target that region by its selector. Many Drupal themes render a header element with role="banner"; custom markup may need a different selector.

If the installation already displays a core Search block, compare the two approaches before enabling this one. A block is simpler to place through the block layout interface and can already work without JavaScript. The JavaScript method in this guide is useful when you want a reusable, consistently labelled form and control its insertion from one attachment point.

Theme, module and URL differences are normal, particularly when maintaining an established site rather than starting a new project. If the search route, language configuration or header markup needs checking, contact Drupalwoo with the Drupal version and relevant template names. That makes a focused support request much easier to diagnose than a general description of a missing search box.

Load the script throughout Drupal

Create a custom module named global_search under modules/custom. The example follows Drupal 10 and 11 conventions. Its machine name should match the PHP function prefix, and its library definition tells Drupal where to find the browser script.

name: Global Search
type: module
description: Adds a site-wide search form.
core_version_requirement: ^10 || ^11

Add the library definition to global_search.libraries.yml:

global-search:
  css:
    theme:
      css/global-search.css: {}
  js:
    js/global-search.js: {}

Install Global Search at /admin/modules and enable it. Put global-search.js in the module’s js directory and create css/global-search.css in its css directory. Drupal caches library definitions in development environments, so clear caches after changing those files.

Next, attach the library on every front-end page and provide the generated search URL. This hook runs while Drupal assembles the page, so the browser receives both pieces of information together. Put the PHP in global_search.module:

use Drupal\Core\Url;

function global_search_attach_libraries(array &$attachments): void {
  $attachments['#attached']['library'][] = 'global-search';
  $attachments['#attached']['drupalSettings']['globalSearch']['searchUrl'] =
    Url::fromRoute('search.page')->toString();
}

The library attachment applies to all pages, including administrative screens unless you add a route check. You may prefer to exclude the administration theme, the node edit form or the search results page itself, where another search field could create confusion. Keep the route in drupalSettings so there is one source of truth for the destination, and do not concatenate query strings manually.

Build an accessible form with plain JavaScript

The script builds a form with a visible text input, a submit button and a hidden label for people using assistive technology. It appends the form to the first banner or header element, or to the body if the theme has no recognisable header. A conventional form action means Drupal receives the query in the same format as its ordinary search form.

Add the following as js/global-search.js inside the module:

(function () {
  'use strict';

  function buildSearchForm() {
    const form = document.createElement('form');
    form.className = 'global-search-form';
    form.setAttribute('role', 'search');
    form.method = 'get';
    form.action = drupalSettings.globalSearch.searchUrl || '/search/node';

    const label = document.createElement('label');
    label.className = 'global-search-label';
    label.htmlFor = 'global-search-input';
    label.textContent = 'Search this site';

    const input = document.createElement('input');
    input.type = 'search';
    input.id = 'global-search-input';
    input.name = 'keys';
    input.placeholder = 'Search this site';

    const button = document.createElement('button');
    button.type = 'submit';
    button.textContent = 'Search';

    form.append(label, input, button);
    return form;
  }

  function addGlobalSearch() {
    if (document.querySelector('.global-search-form')) {
      return;
    }

    const banner = document.querySelector('[role="banner"]') ||
      document.querySelector('header') ||
      document.body;

    banner.appendChild(buildSearchForm());
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', addGlobalSearch);
  } else {
    addGlobalSearch();
  }
})();

The guard prevents a second copy from being inserted when Drupal replaces part of the page through AJAX. The label remains available to screen readers even though it is visually hidden, while the input provides the expected search type and clear control in supporting browsers. The button has visible text, which is more useful than an unlabelled icon.

If your theme places the banner inside a differently named element, change the selector in addGlobalSearch. The fallback to document.body keeps the feature working on an unusually minimal template, although dedicated header markup will produce a better layout. If a server-rendered search form is already present, adapt the selector to enhance that form instead of creating another one.

Make the search bar usable on small screens

Basic styling is enough to make the form usable, but responsive behaviour matters on Australian commutes where mobile coverage can vary. Keep the control in the header on wide screens, allow it to wrap beneath the navigation on narrow screens, and give the text field room for a meaningful phrase. Avoid fixed heights that clip larger browser text settings.

Add these rules to the module’s stylesheet:

.global-search-form {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.global-search-form input {
  min-width: 14rem;
  padding: 0.5rem;
}

.global-search-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

@media (max-width: 48rem) {
  .global-search-form {
    width: 100%;
    flex-wrap: wrap;
  }

  .global-search-form input {
    flex: 1;
    min-width: 0;
  }
}

The media query uses 48rem rather than a device name, so the layout responds to available space instead of a particular handset. A full-width form and flexible input let the button and field share the row without forcing a horizontal scrollbar. Test with long Australian place names, accented characters and screen magnification, not only a short query such as news.

If queries are logged for analytics or troubleshooting, handle them as carefully as other visitor data. When an organisation is covered by the Privacy Act 1988 and the Australian Privacy Principles, a query containing someone’s name can be personal information. A short retention period, restricted access and clear notice reduce unnecessary exposure; state and territory privacy rules may also apply.

Keep these accessibility and usability details intact:

Test and maintain the finished implementation

Test the finished form as an anonymous visitor, a signed-in editor and a user with restricted access. Check the search results page, a phone-sized viewport and a keyboard-only session. A query that should return nothing must still show a useful page, while restricted content must remain absent. Drupal’s own search route should apply its normal indexing and access rules.

Broader Drupal development discussions are useful when deciding whether to replace core Search with a view, a JSON API or a dedicated indexing service. For background and other development perspectives, follow the work of Mihnea B. Any external index should still retain Drupal’s access checks and be documented as part of the site’s content operations.

Use a short verification pass covering these areas:

With those checks complete, the form remains a small, predictable interface over Drupal’s search infrastructure. Editors keep their normal publishing workflow, visitors gain a consistent entry point on every page, and future changes have a clear place in the custom module.