Skip to main content

GL Search

Version: 1.0.18 Package: @gift-card-market/gl-search
Last Updated: July 1, 2026


A reusable React search component with location input and Google Maps integration for Gift Card Market.

Version License

Features

  • 🔍 Search with Location - Integrated Google Maps location autocomplete
  • 🎨 Customizable UI - Configure banner colors, texts, and search button styling
  • Debounced Search - Configurable debounce time for better performance
  • 🎯 Custom Events - Dispatch search and clear events for easy integration
  • 📱 Responsive Design - Works seamlessly on all devices
  • 🌐 Vanilla JS Support - Use as Web Component without React
  • 💪 TypeScript - Full type definitions included

Table of Contents


Installation

Using npm

npm install @gift-card-market/gl-search

Using yarn

yarn add @gift-card-market/gl-search

Integration

React Integration

1. Import the Component and Styles

import { GlSearch } from '@gift-card-market/gl-search';
import '@gift-card-market/gl-search/dist/style.css';

2. Basic Usage

import React from 'react';
import { GlSearch } from '@gift-card-market/gl-search';
import '@gift-card-market/gl-search/dist/style.css';

function App() {
return (
<div>
<GlSearch
environment="development"
jwt="your-jwt-token-here"
/>
</div>
);
}

export default App;

3. Advanced Usage with All Options

import React, { useRef } from 'react';
import { GlSearch } from '@gift-card-market/gl-search';
import type { GlSearchBarHandle } from '@gift-card-market/gl-search';
import '@gift-card-market/gl-search/dist/style.css';

function App() {
const searchRef = useRef<GlSearchBarHandle>(null);

// Configuration data
const searchData = {
banner_color: 'darkblue',
banner_text: [
`Search for your <strong>favorite</strong> restaurants! 🍔`,
`Find the perfect gift card 🎁`,
],
search_text: [
{
content: `(thinking <u>top burger joints</u>?)`,
key: 'burgers',
},
{
content: `(thinking <u>best pizza places</u>?)`,
key: 'pizza',
},
],
search_term: '',
search_button_color: {
text: 'yellow',
background: 'green',
},
};

return (
<div>
<GlSearch
ref={searchRef}
environment="development"
jwt="your-jwt-token-here"
google_api_key="your-google-api-key"
data={searchData}
debounce_ms={250}
query="Location=New York&SearchTerm=pizza"
/>
</div>
);
}

export default App;

Vanilla JS Integration

1. Include Scripts and Styles

After publishing, include the bundled JavaScript and CSS files from the dist/browser folder:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GlSearch - Vanilla JS Demo</title>

<!-- Include the bundled JS and CSS from published package -->
<script src="../../node_modules/@gift-card-market/gl-search/dist/browser/gl-search.global.js"></script>
<link rel="stylesheet" href="../../node_modules/@gift-card-market/gl-search/dist/browser/gl-search.css">
</head>
<body>
<div id="search-container"></div>

<script>
// Your initialization code here
</script>
</body>
</html>

2. Basic Usage

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GlSearch - Vanilla JS Demo</title>

<script src="../../node_modules/@gift-card-market/gl-search/dist/browser/gl-search.global.js"></script>
<link rel="stylesheet" href="../../node_modules/@gift-card-market/gl-search/dist/browser/gl-search.css">
</head>
<body>
<div id="search-container"></div>

<script>
// Initialize the web component
function initializeComponent() {
const container = document.getElementById('search-container');

// Create the custom element
const searchElement = document.createElement('gl-search');

// Set required attributes
searchElement.setAttribute('environment', 'development');
searchElement.setAttribute('jwt', 'your-jwt-token-here');

// Append to container
container.appendChild(searchElement);
}

// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeComponent);
} else {
initializeComponent();
}
</script>
</body>
</html>

3. Advanced Usage with All Options

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GlSearch - Vanilla JS Advanced Demo</title>

<script src="../../node_modules/@gift-card-market/gl-search/dist/browser/gl-search.global.js"></script>
<link rel="stylesheet" href="../../node_modules/@gift-card-market/gl-search/dist/browser/gl-search.css">
</head>
<body>
<div id="search-container"></div>

<script>
// Configuration data
const searchData = {
banner_color: 'darkblue',
banner_text: [
`Search for your <strong>favorite</strong> restaurants! 🍔`,
`Find the perfect gift card 🎁`,
],
search_text: [
{
content: `(thinking <u>top burger joints</u>?)`,
key: 'burgers',
},
{
content: `(thinking <u>best pizza places</u>?)`,
key: 'pizza',
},
],
search_term: '',
search_button_color: {
text: 'yellow',
background: 'green',
},
};

// Store reference to the web component
let searchElement = null;

// Initialize the web component
function initializeComponent() {
const container = document.getElementById('search-container');

// Create the custom element
searchElement = document.createElement('gl-search');

// Set required attributes
searchElement.setAttribute('environment', 'development');
searchElement.setAttribute('jwt', 'your-jwt-token-here');

// Set optional attributes
searchElement.setAttribute('google_api_key', 'your-google-api-key');
searchElement.setAttribute('debounce_ms', '250');
searchElement.setAttribute('query', 'Location=New York&SearchTerm=pizza');

// Set data as JSON string
searchElement.setAttribute('data', JSON.stringify(searchData));

// Append to container
container.appendChild(searchElement);

console.log('Web Component initialized successfully');
}

// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeComponent);
} else {
initializeComponent();
}
</script>
</body>
</html>

API Reference

Props / Attributes

PropertyTypeRequiredDefaultDescription
environmentstring✅ Yes-Environment mode: 'development', 'qa', 'staging' or 'production'
jwtstring✅ Yes-JWT authentication token
google_api_keystringNo-Google Maps API key for location autocomplete
debounce_msnumberNo250Debounce time in milliseconds for search input
querystringNo-Initial search query (format: 'Location=...&SearchTerm=...')
dataobjectNo{}Configuration object (see below)

Data Object Structure

{
banner_color?: string; // Banner background color
banner_text?: string[]; // Array of rotating banner texts (supports HTML)
search_text?: Array<{ // Array of search placeholder suggestions
content: string; // Placeholder text (supports HTML)
key: string; // Search term key
}>;
search_term?: string; // Initial search term
search_button_color?: { // Search button styling
text: string; // Text color
background: string; // Background color
};
}

Ref Methods (React Only)

When using React, you can access component methods via ref:

interface GlSearchBarHandle {
focus: () => void; // Focus on the location input field
setQuery: (query: string) => void; // Set search query programmatically
setCardPrograms: (cardPrograms: string) => void; // Set card programs (defaults to 'restaurant;spa' if not set)
}

Example Usage

import React, { useRef } from 'react';
import { GlSearch } from '@gift-card-market/gl-search';
import type { GlSearchBarHandle } from '@gift-card-market/gl-search';

function App() {
const searchRef = useRef<GlSearchBarHandle>(null);

const handleFocus = () => {
searchRef.current?.focus();
};

const handleSetQuery = () => {
searchRef.current?.setQuery('Location=New York&SearchTerm=pizza');
};

const handleSetCardPrograms = () => {
searchRef.current?.setCardPrograms('spa;choose_your_own_burger');
};

return (
<div>
<button onClick={handleFocus}>Focus Input</button>
<button onClick={handleSetQuery}>Set Query</button>
<button onClick={handleSetCardPrograms}>Set Card Programs</button>

<GlSearch
ref={searchRef}
environment="development"
jwt="your-jwt-token"
/>
</div>
);
}

Vanilla JS Methods

In Vanilla JS, you can call methods directly on the DOM element:

const searchElement = document.querySelector('gl-search');

// Focus on input
searchElement.focus();

// Set query
searchElement.setQuery('Location=New York&SearchTerm=pizza');

// Set card programs
searchElement.setCardPrograms('spa;choose_your_own_burger');

Custom Events & Shadow DOM

Shadow DOM Isolation

The Web Component wrapper (gl-search) uses Shadow DOM (mode: 'open') to completely encapsulate styling. This prevents outside CSS styles from bleeding into the component and vice versa. Component styles are compiled and injected directly into the shadow root during build execution, so you do not need to link an external stylesheet for the Web Component.

Custom Events

Custom DOM Events are dispatched directly on the custom element host instance and bubble up with composed: true:

Event NameDescriptionEvent Detail
gl:searchDispatched when a search is performed (either by clicking Search or pressing Enter in the search input){ query: string }
search_bar.searchGlobal event dispatched for Search Results component{ data: { location, term, sort_by, provider_id, card_programs }, timestamp }
gl:clearDispatched when the search is cleared{}

Note: The card_programs value defaults to 'restaurant;spa' if not explicitly set via the setCardPrograms() method.

You can listen to these events directly on the Web Component instance (recommended):

const searchComponent = document.querySelector('gl-search');
searchComponent.addEventListener('gl:search', (e) => {
console.log('Search triggered with query:', e.detail.query);
});

Event Handling

React Event Handling

import React, { useEffect } from 'react';
import { GlSearch, SearchBarEvents } from '@gift-card-market/gl-search';
import '@gift-card-market/gl-search/dist/style.css';

function App() {
useEffect(() => {
// Listen to search event
const handleSearch = (e: CustomEvent) => {
console.log('Search performed:', e.detail);
// e.detail.query contains: "Location=...&SearchTerm=...&SortBy=...&ProviderId=..."

// Parse query string
const params = new URLSearchParams(e.detail.query);
const location = params.get('Location');
const searchTerm = params.get('SearchTerm');

console.log('Location:', location);
console.log('Search Term:', searchTerm);

// Perform your search logic here
// e.g., fetch data from API, update state, etc.
};

// Listen to clear event
const handleClear = (e: CustomEvent) => {
console.log('Search cleared:', e.detail);
// Reset your search results, state, etc.
};

// Add event listeners
document.addEventListener(SearchBarEvents.SEARCH, handleSearch as EventListener);
document.addEventListener(SearchBarEvents.CLEAR, handleClear as EventListener);

// Cleanup
return () => {
document.removeEventListener(SearchBarEvents.SEARCH, handleSearch as EventListener);
document.removeEventListener(SearchBarEvents.CLEAR, handleClear as EventListener);
};
}, []);

return (
<div>
<GlSearch
environment="development"
jwt="your-jwt-token"
/>
</div>
);
}

export default App;

Vanilla JS Event Handling

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GlSearch - Event Handling</title>

<!-- Include Web Component script (CSS is automatically injected via Shadow DOM) -->
<script src="../../node_modules/@gift-card-market/gl-search/dist/browser/gl-search.global.js"></script>
</head>
<body>
<div id="search-container"></div>

<script>
// Initialize the component
function initializeComponent() {
const container = document.getElementById('search-container');
const searchElement = document.createElement('gl-search');

searchElement.setAttribute('environment', 'development');
searchElement.setAttribute('jwt', 'your-jwt-token');

container.appendChild(searchElement);
}

// Listen to search event
document.addEventListener('gl:search', function(e) {
console.log('Search performed:', e.detail);

// Parse query string
const params = new URLSearchParams(e.detail.query);
const location = params.get('Location');
const searchTerm = params.get('SearchTerm');

console.log('Location:', location);
console.log('Search Term:', searchTerm);

// Perform your search logic here
// e.g., fetch data from API, update UI, etc.
});

// Listen to clear event
document.addEventListener('gl:clear', function(e) {
console.log('Search cleared:', e.detail);
// Reset your search results, UI, etc.
});

// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeComponent);
} else {
initializeComponent();
}
</script>
</body>
</html>

Complete Examples

React Complete Example

import React, { useRef, useEffect, useState } from 'react';
import { GlSearch, SearchBarEvents } from '@gift-card-market/gl-search';
import type { GlSearchBarHandle } from '@gift-card-market/gl-search';
import '@gift-card-market/gl-search/dist/style.css';

function App() {
const searchRef = useRef<GlSearchBarHandle>(null);
const [searchResults, setSearchResults] = useState([]);
const [loading, setLoading] = useState(false);

// Configuration
const searchData = {
banner_color: 'darkblue',
banner_text: [
`Do they love burgers? 🍔 Search for their <strong style="color: #ffe606">favorite</strong> below.`,
`You've got <strong style="color: #ffe606">great taste</strong>. We've got gift cards to match.`,
],
search_text: [
{
content: `(thinking <u>top smash burger joints</u>?)`,
key: 'smash burgers',
},
{
content: `(thinking <u>best lunch spots near me</u>?)`,
key: 'best lunch spots',
},
],
search_term: '',
search_button_color: {
text: 'yellow',
background: 'green',
},
};

useEffect(() => {
// Handle search event
const handleSearch = async (e: CustomEvent) => {
console.log('Search event:', e.detail);

setLoading(true);

try {
// Parse query parameters
const params = new URLSearchParams(e.detail.query);
const location = params.get('Location');
const searchTerm = params.get('SearchTerm');

// Call your API
const response = await fetch(`/api/search?location=${location}&term=${searchTerm}`);
const data = await response.json();

setSearchResults(data.results);
} catch (error) {
console.error('Search error:', error);
} finally {
setLoading(false);
}
};

// Handle clear event
const handleClear = (e: CustomEvent) => {
console.log('Clear event:', e.detail);
setSearchResults([]);
};

// Add listeners
document.addEventListener(SearchBarEvents.SEARCH, handleSearch as EventListener);
document.addEventListener(SearchBarEvents.CLEAR, handleClear as EventListener);

// Cleanup
return () => {
document.removeEventListener(SearchBarEvents.SEARCH, handleSearch as EventListener);
document.removeEventListener(SearchBarEvents.CLEAR, handleClear as EventListener);
};
}, []);

// Handler functions for buttons
const handleFocus = () => {
searchRef.current?.focus();
};

const handleSetQuery = () => {
searchRef.current?.setQuery('Location=New York&SearchTerm=pizza');
};

return (
<div className="app">
{/* Control buttons */}
<div style={{ padding: '20px', background: '#f5f5f5' }}>
<button onClick={handleFocus}>Focus Input</button>
<button onClick={handleSetQuery}>Set Query (NY/Pizza)</button>
</div>

{/* Search component */}
<GlSearch
ref={searchRef}
environment="development"
jwt="your-jwt-token"
google_api_key="your-google-api-key"
data={searchData}
debounce_ms={250}
/>

{/* Results */}
{loading && <div>Loading...</div>}
{searchResults.length > 0 && (
<div className="results">
{searchResults.map((result, index) => (
<div key={index}>{/* Render your results */}</div>
))}
</div>
)}
</div>
);
}

export default App;

Vanilla JS Complete Example

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GlSearch - Complete Vanilla JS Example</title>

<!-- Include Web Component script (CSS is automatically injected via Shadow DOM) -->
<script src="../../node_modules/@gift-card-market/gl-search/dist/browser/gl-search.global.js"></script>

<style>
body {
margin: 0;
font-family: Arial, sans-serif;
}
.control-panel {
padding: 20px;
background: #f5f5f5;
border-bottom: 2px solid #ddd;
}
.control-panel button {
margin-right: 10px;
padding: 10px 20px;
background: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.results {
padding: 20px;
}
.loading {
text-align: center;
padding: 20px;
font-size: 18px;
}
</style>
</head>
<body>
<!-- Control Panel -->
<div class="control-panel">
<button onclick="handleFocus()">🎯 Focus Input</button>
<button onclick="handleSetQuery()">📝 Set Query (NY/Pizza)</button>
</div>

<!-- Search Container -->
<div id="search-container"></div>

<!-- Results Container -->
<div id="results-container"></div>

<script>
// Configuration
const searchData = {
banner_color: 'darkblue',
banner_text: [
`Do they love burgers? 🍔 Search for their <strong style="color: #ffe606">favorite</strong> below.`,
`You've got <strong style="color: #ffe606">great taste</strong>. We've got gift cards to match.`,
],
search_text: [
{
content: `(thinking <u>top smash burger joints</u>?)`,
key: 'smash burgers',
},
{
content: `(thinking <u>best lunch spots near me</u>?)`,
key: 'best lunch spots',
},
],
search_term: '',
search_button_color: {
text: 'yellow',
background: 'green',
},
};

// Store reference to the web component
let searchElement = null;

// Initialize the web component
function initializeComponent() {
const container = document.getElementById('search-container');

// Create the custom element
searchElement = document.createElement('gl-search');

// Set required attributes
searchElement.setAttribute('environment', 'development');
searchElement.setAttribute('jwt', 'your-jwt-token');

// Set optional attributes
searchElement.setAttribute('google_api_key', 'your-google-api-key');
searchElement.setAttribute('debounce_ms', '250');
searchElement.setAttribute('data', JSON.stringify(searchData));

// Append to container
container.appendChild(searchElement);

console.log('Component initialized');
}

// Handle search event
document.addEventListener('gl:search', async function(e) {
console.log('Search performed:', e.detail);

const resultsContainer = document.getElementById('results-container');
resultsContainer.innerHTML = '<div class="loading">Loading...</div>';

try {
// Parse query parameters
const params = new URLSearchParams(e.detail.query);
const location = params.get('Location');
const searchTerm = params.get('SearchTerm');

console.log('Location:', location);
console.log('Search Term:', searchTerm);

// Call your API
const response = await fetch(`/api/search?location=${location}&term=${searchTerm}`);
const data = await response.json();

// Display results
resultsContainer.innerHTML = '<div class="results"><h2>Search Results</h2>' +
data.results.map(result => `<div>${result.name}</div>`).join('') +
'</div>';

} catch (error) {
console.error('Search error:', error);
resultsContainer.innerHTML = '<div class="results">Error loading results</div>';
}
});

// Handle clear event
document.addEventListener('gl:clear', function(e) {
console.log('Search cleared:', e.detail);
document.getElementById('results-container').innerHTML = '';
});

// Method to focus input
function handleFocus() {
if (searchElement) {
searchElement.focus();
console.log('Input focused');
}
}

// Method to set query
function handleSetQuery() {
if (searchElement) {
searchElement.setQuery('Location=New York&SearchTerm=pizza');
console.log('Query set');
}
}

// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeComponent);
} else {
initializeComponent();
}
</script>
</body>
</html>

TypeScript Support

This package includes full TypeScript definitions. Import types as needed:

import type {
GlSearchProps,
GlSearchBarHandle,
SearchBarEventType
} from '@gift-card-market/gl-search';
import { SearchBarEvents } from '@gift-card-market/gl-search';

Browser Support

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Edge (latest)

License

ISC © Gift Card Market


Support

For issues and questions, please visit:


Contributing

Contributions are welcome! Please refer to the project's contributing guidelines.


Changelog

Version 1.0.18 (Current)

  • 🔒 Encapsulated CSS bleeding fixes (scoped variables and nested selectors).
  • ⚙️ Added Tailwind utility prefix and configured important option.

Version 1.0.16

  • ✨ Added support for pressing Enter in the search term field to immediately trigger a precise search using the active location.
  • 🔒 Enabled complete Shadow DOM encapsulation to protect styling from outside layout bleeding.
  • 📢 Custom events now bubble up and dispatch directly on the custom element host wrapper.