Building A Search Modal for Membership-Gated WordPress Sites

Most WordPress search tutorials stop after you add a widget to a header.

This fails when you have gated content like paid courses or private videos. A default search will leak the titles of your private material to logged-out visitors.

I built a live search modal for a fitness membership site. The site uses WordPress, WooCommerce, Divi, LearnDash, and WishList Member.

Here is how to build search that respects your paywall.

The Architecture

I used a unified index with access-aware filtering. I chose this to ensure a logged-out user never sees a title or excerpt from a member-only post.

The UI uses an icon that opens a full-screen overlay. This saves space on mobile and looks cleaner than a cramped input bar.

The Backend

Everything runs through a single custom REST route in a child theme.

Key technical rules:

  • Guard dependencies: Use a function check for search plugins like Relevanssi. If the plugin is missing, the search should fall back to core WordPress instead of breaking the site.
  • Server-side filtering: Never filter results using JavaScript. If you hide a result in the browser, the data is already in the network response. Anyone with DevTools can see it. Filter the data before the server sends the response.
  • Cache exclusion: You must exclude your search REST route from page caching. If you cache the results, one member's search could be served to a guest, leaking private content.

The Frontend

The client side uses vanilla JavaScript.

Three things make the UX work:

  • Debounce: Wait 250ms after a keystroke before sending a request. This stops unnecessary server load.
  • AbortController: Cancel previous requests when a user keeps typing. This prevents old results from overwriting new ones.
  • Error states: Show a clear message if a fetch fails. A spinning loader that never stops is bad UX.

The Mobile Lesson

I tried to inject the search button into the Divi mobile menu. The theme intercepted the clicks, making the button unclickable.

The fix was simple: Stop fighting the theme.

Instead of putting the button inside the menu, I injected it into the header as a standalone element. This kept it outside the theme's control and made it easy to tap.

Summary

  • Access control must happen on the server.
  • Exclude search endpoints from your cache.
  • Use debounce and AbortController to handle requests.
  • Escape all API data before injecting it into the DOM to prevent XSS.
  • If a page builder resists your code, place your element beside it rather than inside it.

Source: https://dev.to/highcenburg/building-a-search-modal-for-a-membership-gated-wordpress-site-b92