fervor [>]CODING & CURIOSITY
FERVOR LEARNING SYSTEMTUTORIALS
← WordPress

WordPress / 6 MIN READ

Search Bar Rest API

Creating a Search Bar with Rest API

From the original Fervor library. Examples may use older package versions.

πŸ” Tutorial: Cross-Site Product Search Using the WordPress REST API

Search products on store.example.com from example.com


🧰 What You Need

Requirement Setup
Site A example.com – where your users will search
Site B store.example.com – where the WooCommerce products live
API Access WordPress REST API is built-in and enabled by default
(Optional) Use CoCart or custom endpoint if you want more product data (price, images, etc.)

βœ… Step 1: Test the REST API on store.example.com

Open this in your browser:

https://store.example.com/wp-json/wp/v2/product?s=shirt

You should see JSON data. Each product entry will include:

  • title.rendered
  • excerpt.rendered
  • link

βœ… Step 2: Add a Search Form on example.com

Paste this HTML anywhere on example.com β€” in a page, template, or widget:

<form id="product-search-form">
  <input type="text" id="search-query" placeholder="Search products from store..." />
  <button type="submit">Search</button>
</form>

<div id="product-search-results"></div>

βœ… Step 3: Add JavaScript to Fetch Results from store.example.com

Paste this in the footer or enqueue it properly in a separate JS file:

<script>
document.getElementById('product-search-form').addEventListener('submit', function (e) {
  e.preventDefault();

  const query = document.getElementById('search-query').value;
  const resultsContainer = document.getElementById('product-search-results');

  resultsContainer.innerHTML = 'Searching...';

  fetch(`https://store.example.com/wp-json/wp/v2/product?s=${encodeURIComponent(query)}`)
    .then(response => response.json())
    .then(products => {
      if (!products.length) {
        resultsContainer.innerHTML = '<p>No products found.</p>';
        return;
      }

      const resultsHTML = products.map(product => `
        <div class="product-result">
          <h3><a href="${product.link}" target="_blank">${product.title.rendered}</a></h3>
          <p>${product.excerpt.rendered}</p>
        </div>
      `).join('');

      resultsContainer.innerHTML = resultsHTML;
    })
    .catch(error => {
      console.error('Search error:', error);
      resultsContainer.innerHTML = '<p>There was an error fetching results.</p>';
    });
});
</script>

βœ… Step 4: (Optional) Style the Search Results

Add this CSS to your theme’s Additional CSS section:

.product-result {
  padding: 10px;
  border-bottom: 1px solid #ccc;
  margin-bottom: 10px;
}

.product-result h3 {
  margin: 0 0 5px;
  font-size: 18px;
}

.product-result p {
  margin: 0;
  color: #555;
}

πŸš€ Bonus: Add Price, Images, etc.

By default, the WordPress REST API won’t return product price or image. To get those, either:

  1. Use a plugin like CoCart or WP REST API Controller

  2. Or write a custom REST API endpoint on store.example.com that returns:

    • title
    • image URL
    • price
    • permalink

Let me know if you want a working custom endpoint example β€” I’ll build one for you.


βœ… Summary

Step Description
πŸ”Ž REST API Fetch products from store.example.com
🧾 Search Form Add form to example.com
βš™οΈ JavaScript Make the API request and render results
πŸ”— Results Link each result back to store.example.com/product/...

#Bonus Time

Let’s upgrade this search so it also returns product thumbnails and prices from store.example.com, and still displays them on example.com.

Since the default WordPress REST API doesn’t expose price or image, we’ll create a custom REST endpoint on store.example.com that returns:

  • Product title
  • Permalink
  • Price
  • Product image URL
  • Excerpt/description

βš™οΈ PART 1: Create the Custom REST API Endpoint on store.example.com

Add this to your theme’s functions.php or in a custom plugin on store.example.com:

add_action('rest_api_init', function () {
  register_rest_route('custom/v1', '/product-search', [
    'methods'  => 'GET',
    'callback' => 'custom_product_search',
    'permission_callback' => '__return_true', // Public access
  ]);
});

function custom_product_search($request) {
  $term = sanitize_text_field($request->get_param('s'));

  $query = new WP_Query([
    'post_type'      => 'product',
    'posts_per_page' => 10,
    's'              => $term,
    'post_status'    => 'publish',
  ]);

  $results = [];

  while ($query->have_posts()) {
    $query->the_post();
    global $product;

    $product_obj = wc_get_product(get_the_ID());

    $results[] = [
      'title'   => get_the_title(),
      'excerpt' => get_the_excerpt(),
      'link'    => get_permalink(),
      'price'   => $product_obj ? $product_obj->get_price_html() : '',
      'image'   => get_the_post_thumbnail_url(get_the_ID(), 'medium'),
    ];
  }

  wp_reset_postdata();
  return $results;
}

βœ… This creates a public endpoint:

https://store.example.com/wp-json/custom/v1/product-search?s=your+query

πŸ’» PART 2: Update example.com to Use the New API and Display Price + Image

βœ… HTML (same as before):

<form id="product-search-form">
  <input type="text" id="search-query" placeholder="Search products from store..." />
  <button type="submit">Search</button>
</form>

<div id="product-search-results"></div>

βœ… JavaScript (enhanced):

<script>
document.getElementById('product-search-form').addEventListener('submit', function (e) {
  e.preventDefault();

  const query = document.getElementById('search-query').value;
  const resultsContainer = document.getElementById('product-search-results');

  resultsContainer.innerHTML = 'Searching...';

  fetch(`https://store.example.com/wp-json/custom/v1/product-search?s=${encodeURIComponent(query)}`)
    .then(response => response.json())
    .then(products => {
      if (!products.length) {
        resultsContainer.innerHTML = '<p>No products found.</p>';
        return;
      }

      const resultsHTML = products.map(product => `
        <div class="product-result">
          <a href="${product.link}" target="_blank">
            <img src="${product.image}" alt="${product.title}" style="max-width: 150px; display: block; margin-bottom: 10px;">
            <h3>${product.title}</h3>
          </a>
          <p>${product.excerpt}</p>
          <strong>${product.price}</strong>
        </div>
      `).join('');

      resultsContainer.innerHTML = resultsHTML;
    })
    .catch(error => {
      console.error('Search error:', error);
      resultsContainer.innerHTML = '<p>There was an error fetching results.</p>';
    });
});
</script>

βœ… Optional CSS:

.product-result {
  padding: 15px;
  border: 1px solid #eee;
  margin-bottom: 15px;
  border-radius: 5px;
  background: #fafafa;
}
.product-result img {
  max-width: 100%;
  height: auto;
}
.product-result h3 {
  margin: 10px 0 5px;
}
.product-result p {
  margin: 0 0 10px;
}

🏁 Final Result

  • User searches from example.com
  • The form queries store.example.com via your custom API
  • Results show image, title, price, excerpt
  • Clicking sends the user to the product page on store.example.com

Let’s take this to the next level by adding category filtering and pagination to your remote product search system.


πŸ” Upgraded Tutorial:

Cross-Site Product Search with Category Filter + Pagination From example.com β†’ Fetching products on store.example.com


βš™οΈ PART 1: Update the Custom REST API Endpoint (on store.example.com)

In your theme’s functions.php or a custom plugin, replace the previous endpoint code with this enhanced version:

add_action('rest_api_init', function () {
  register_rest_route('custom/v1', '/product-search', [
    'methods'  => 'GET',
    'callback' => 'custom_product_search',
    'permission_callback' => '__return_true',
  ]);
});

function custom_product_search($request) {
  $term      = sanitize_text_field($request->get_param('s'));
  $category  = sanitize_text_field($request->get_param('category'));
  $paged     = absint($request->get_param('page')) ?: 1;

  $args = [
    'post_type'      => 'product',
    'posts_per_page' => 5,
    'paged'          => $paged,
    's'              => $term,
    'post_status'    => 'publish',
  ];

  if (!empty($category)) {
    $args['tax_query'] = [[
      'taxonomy' => 'product_cat',
      'field'    => 'slug',
      'terms'    => $category,
    ]];
  }

  $query = new WP_Query($args);
  $results = [];

  while ($query->have_posts()) {
    $query->the_post();
    $product = wc_get_product(get_the_ID());

    $results[] = [
      'title'   => get_the_title(),
      'excerpt' => get_the_excerpt(),
      'link'    => get_permalink(),
      'price'   => $product ? $product->get_price_html() : '',
      'image'   => get_the_post_thumbnail_url(get_the_ID(), 'medium'),
    ];
  }

  wp_reset_postdata();

  return [
    'products'    => $results,
    'total_pages' => $query->max_num_pages,
    'current_page'=> $paged,
  ];
}

βœ… This now supports:

  • ?s=term
  • &category=category-slug
  • &page=2

🌐 PART 2: Update the Search Form on example.com

Add category selection and pagination:

<form id="product-search-form">
  <input type="text" id="search-query" placeholder="Search products..." />
  
  <select id="category-filter">
    <option value="">All Categories</option>
    <option value="shirts">Shirts</option>
    <option value="hats">Hats</option>
    <option value="accessories">Accessories</option>
  </select>

  <button type="submit">Search</button>
</form>

<div id="product-search-results"></div>
<div id="pagination-controls"></div>

🧠 PART 3: JavaScript – Add Filtering & Pagination Support

<script>
let currentPage = 1;

function fetchProducts(page = 1) {
  const query = document.getElementById('search-query').value;
  const category = document.getElementById('category-filter').value;
  const resultsContainer = document.getElementById('product-search-results');
  const paginationContainer = document.getElementById('pagination-controls');

  resultsContainer.innerHTML = 'Searching...';
  paginationContainer.innerHTML = '';

  const apiUrl = `https://store.example.com/wp-json/custom/v1/product-search?s=${encodeURIComponent(query)}&category=${encodeURIComponent(category)}&page=${page}`;

  fetch(apiUrl)
    .then(res => res.json())
    .then(data => {
      const products = data.products;
      const totalPages = data.total_pages;

      if (!products.length) {
        resultsContainer.innerHTML = '<p>No products found.</p>';
        return;
      }

      const productHTML = products.map(product => `
        <div class="product-result">
          <a href="${product.link}" target="_blank">
            <img src="${product.image}" alt="${product.title}" style="max-width: 150px;">
            <h3>${product.title}</h3>
          </a>
          <p>${product.excerpt}</p>
          <strong>${product.price}</strong>
        </div>
      `).join('');

      resultsContainer.innerHTML = productHTML;

      // Pagination
      let paginationHTML = '';
      for (let i = 1; i <= totalPages; i++) {
        paginationHTML += `<button class="page-btn" data-page="${i}" ${i === page ? 'disabled' : ''}>${i}</button>`;
      }
      paginationContainer.innerHTML = paginationHTML;

      document.querySelectorAll('.page-btn').forEach(btn => {
        btn.addEventListener('click', () => {
          currentPage = parseInt(btn.dataset.page);
          fetchProducts(currentPage);
        });
      });
    })
    .catch(err => {
      console.error(err);
      resultsContainer.innerHTML = '<p>Error fetching results.</p>';
    });
}

document.getElementById('product-search-form').addEventListener('submit', function (e) {
  e.preventDefault();
  currentPage = 1;
  fetchProducts(currentPage);
});
</script>

βœ… Optional CSS for Pagination

#pagination-controls {
  margin-top: 20px;
}
#pagination-controls button {
  margin: 0 4px;
  padding: 5px 10px;
  cursor: pointer;
}
#pagination-controls button[disabled] {
  background: #ddd;
  cursor: default;
}

🏁 Final Features Enabled

βœ… Real-time WooCommerce product search from store.example.com βœ… Links open original product pages βœ… Thumbnail + price βœ… Category filter βœ… Pagination for clean UX


Keep your curiosity going.Explore more WordPress β†’
287 TUTORIALS Β· 22 TOPICSREADY