Installation & Setup
Quick Start
SEO Select is available as an NPM package and can be integrated into any modern web project. Choose your preferred installation method and framework below.
NPM Installation
Install via NPM for most JavaScript projects:
npm install seo-select
Basic Import & Usage
After installation, import the components and TypeScript types into your project:
// Import the components (auto-registers custom elements)
import 'seo-select';
// Import search-enabled select component
import 'seo-select/components/seo-select-search';
// Import Style
import 'seo-select/styles'
// Import TypeScript types for better development experience
import 'seo-select/types';
// Optional: Import specific component types for custom integrations
import type { SeoSelectElement, SeoSelectSearchElement } from 'seo-select/types';
HTML Usage Examples
Once imported, use the components directly in your HTML with full TypeScript support:
<seo-select name="example" width="200px">
<option value="option1">Option 1</option>
<option value="option2" selected>Option 2</option>
<option value="option3">Option 3</option>
</seo-select>
<seo-select-search multiple name="skills" width="300px">
<option value="js">JavaScript</option>
<option value="ts">TypeScript</option>
<option value="react">React</option>
</seo-select-search>
<seo-select
dark
theme="float"
texts='{"placeholder": "Choose an option"}'
name="themed">
<option value="1">Dark Option 1</option>
<option value="2">Dark Option 2</option>
</seo-select>
JavaScript API Usage
Programmatic control and dynamic option management:
// Get element reference
const selectElement = document.querySelector('seo-select[name="example"]');
// Set options programmatically
selectElement.optionItems = [
{ value: 'js', label: 'JavaScript' },
{ value: 'ts', label: 'TypeScript' },
{ value: 'react', label: 'React' }
];
// Set/get current selection
selectElement.value = 'js';
console.log(selectElement.value); // 'js'
// Multiple selection
selectElement.selectedValues = ['js', 'react'];
console.log(selectElement.selectedValues); // ['js', 'react']
// Dynamic option management
selectElement.addOption({ value: 'vue', label: 'Vue.js' });
selectElement.clearOption('js');
selectElement.clearAllOptions();
Event Handling
Comprehensive event system for user interactions and form integration:
const selectElement = document.querySelector('seo-select[name="example"]');
// Custom component events
selectElement.addEventListener('onSelect', (event) => {
console.log('Selected:', event.detail);
// { label: 'JavaScript', value: 'js' }
});
selectElement.addEventListener('onDeselect', (event) => {
console.log('Deselected:', event.detail);
});
selectElement.addEventListener('onReset', (event) => {
console.log('Reset to:', event.detail);
});
// Standard form events
selectElement.addEventListener('change', (event) => {
console.log('Form change:', event.target.value);
});
// For search components
const searchElement = document.querySelector('seo-select-search');
searchElement.addEventListener('onSearch', (event) => {
console.log('Search query:', event.detail.query);
});
Basic Select Usage
Overview
SEO Select provides two main implementation methods: the recommended slot-based approach using standard HTML option tags, and a fallback array-based method for dynamic content. Both methods support full keyboard navigation, form integration, and accessibility features.
Slot Method (Recommended)
The slot-based approach uses standard HTML <option> tags as children, providing semantic HTML structure and better SEO compatibility. This method maintains the original HTML form behavior while adding enhanced functionality.
<seo-select name="brand" width="200px">
<option value="kia">Kia Motors</option>
<option value="hyundai" selected>Hyundai Motor</option>
<option value="bmw">BMW</option>
<option value="benz">Mercedes-Benz</option>
</seo-select>
Array Method (Fallback)
When slot content is not available or when options need to be populated dynamically through JavaScript, the array method provides a programmatic interface using the optionItems property.
// JavaScript to populate the select
const selectElement = document.querySelector('seo-select[name="brand-alt"]');
selectElement.optionItems = [
{ value: 'kia', label: 'Kia Motors' },
{ value: 'hyundai', label: 'Hyundai Motor' },
{ value: 'bmw', label: 'BMW' },
{ value: 'benz', label: 'Mercedes-Benz' }
];
selectElement.value = 'hyundai';
Keyboard Navigation & Accessibility
Full keyboard navigation support includes Tab for focus, Arrow keys for option navigation, Enter for selection, Escape to close, and Space for dropdown toggle. Screen reader compatible with proper ARIA attributes.
Event Handling System
Comprehensive event system with custom events (onSelect, onDeselect, onReset) and standard form events (change, input). All events provide detailed information about the selection state and can be used for form validation and data processing.
Search Select Components
Multilingual Search Engine
Advanced search functionality supporting Korean initial consonants (초성), Japanese romaji conversion, Chinese pinyin, and fuzzy English matching. Optimized for large datasets with virtual scrolling and intelligent caching.
Multilingual Search Capabilities
The search system supports multiple languages and input methods: Korean initial consonant matching (ㅅㅇ → 서울), Japanese romaji conversion (tokyo → 東京), Chinese pinyin support, and English fuzzy matching. Case-insensitive with accent normalization.
- Korean: "서울", "ㅅㅇ" (initial consonants), "런던", "ㄹㄷ"
- Japanese: "東京", "とうきょう", "tokyo" (romaji)
- Chinese: "北京", "上海", "beijing", "shanghai"
- English: "new", "NEW", "london" (case insensitive)
Asynchronous Loading States
Built-in loading state management for asynchronous data fetching. Displays loading indicators and handles empty states gracefully. Supports progressive loading and lazy data fetching patterns.
Virtual Scrolling for Large Datasets
Efficient rendering of large datasets (10,000+ items) using virtual scrolling techniques. Only renders visible items to maintain smooth performance. Includes intelligent search indexing and result caching for instant response times.
Theme System
Adaptive Theme Engine
Flexible theming system with light/dark mode support, customizable color schemes, and responsive design patterns. Themes automatically adapt to system preferences and can be dynamically switched at runtime.
Float Theme (Default)
Modern floating design with subtle shadows, smooth animations, and rounded corners. Optimized for contemporary UI designs with enhanced visual hierarchy and depth perception.
Basic Theme
Clean, minimal design with sharp edges and immediate visual feedback. Perfect for data-dense applications and professional interfaces where clarity and simplicity are prioritized.
Dark Mode - Float Theme
Dark theme variant with optimized contrast ratios, reduced eye strain colors, and enhanced focus indicators. Automatically adapts to system dark mode preferences.
Dark Mode - Search Select
Dark theme search component with enhanced search input styling, improved placeholder text visibility, and optimized dropdown contrast for better multilingual text readability.
Dynamic Theme Switching
Runtime theme switching capabilities allow dynamic adaptation to user preferences or system changes. Themes transition smoothly without layout shifts or visual glitches.
Multiple Selection
Advanced Multi-Selection System
Sophisticated multiple selection with tag-based display, batch operations, and intelligent selection management. Supports keyboard shortcuts, bulk actions, and customizable selection limits.
Tag-Based Multiple Selection
Multiple selection with visual tag representation. Each selected item appears as a removable tag with individual deselection capability. Supports drag-and-drop reordering and bulk operations.
Multiple Selection with Multilingual Search
Combines the power of multilingual search with multiple selection capabilities. Users can search in their preferred language while building complex selection sets. Includes intelligent duplicate prevention and selection validation.
Dark Theme Multiple Selection
Dark theme variant optimized for multiple selection interfaces. Enhanced tag visibility, improved contrast for selected states, and better focus indicators for keyboard navigation in multi-select scenarios.
Dynamic Option Management
Real-time Option Manipulation
Advanced dynamic option management system supporting real-time option addition, removal, and updates with instant virtual scroll synchronization. Ideal for applications requiring live data updates, user-generated content, or API-driven option sets.
Batch Option Management
Replace all options at once while optionally preserving current selections. Perfect for loading new datasets from APIs, switching between different option categories, or implementing filtered views with state management.
Advanced Features
Essential Features
Provides core functionality such as easy setup, responsive design, and simple customization options. Ideal for building straightforward applications with common requirements.
Form Integration
Complete form integration with validation, error handling, and submission management. Supports HTML5 validation attributes, custom validators, and integration with popular form libraries and frameworks.
Framework Wrappers
Official Framework Wrappers
SEO Select provides official wrapper components for major frameworks. Each wrapper offers native-feeling APIs with proper event handling and TypeScript support. All framework dependencies are optional peer dependencies.
React Wrapper
Use the official React wrapper for native React experience with proper event handling:
import { SeoSelect, SeoSelectSearch } from 'seo-select/react';
import 'seo-select/styles';
export default function MyComponent() {
const options = [
{ value: 'react', label: 'React' },
{ value: 'nextjs', label: 'Next.js' },
{ value: 'remix', label: 'Remix' }
];
return (
<SeoSelect
name="framework"
theme="float"
language="ko"
optionItems={options}
onSelect={(e) => console.log('Selected:', e.detail)}
onReset={(e) => console.log('Reset:', e.detail)}
/>
);
}
// With Search and Ref
import { useRef } from 'react';
import { SeoSelectSearch, type SeoSelectRef } from 'seo-select/react';
function SearchExample() {
const selectRef = useRef<SeoSelectRef>(null);
return (
<SeoSelectSearch
ref={selectRef}
name="city"
multiple
showReset
optionItems={[
{ value: 'seoul', label: 'Seoul' },
{ value: 'tokyo', label: 'Tokyo' }
]}
onSelect={(e) => console.log('Selected:', e.detail)}
onSearchChange={(e) => console.log('Search:', e.detail.searchText)}
/>
);
}
Vue 3 Wrapper
Use the official Vue wrapper with Composition API support:
<script setup lang="ts">
import { SeoSelect, SeoSelectSearch } from 'seo-select/vue';
import 'seo-select/styles';
const options = [
{ value: 'vue', label: 'Vue 3' },
{ value: 'nuxt', label: 'Nuxt 3' },
{ value: 'vite', label: 'Vite' }
];
const handleSelect = (detail: { label: string; value: string }) => {
console.log('Selected:', detail);
};
</script>
<template>
<SeoSelect
name="framework"
theme="float"
:optionItems="options"
@select="handleSelect"
/>
</template>
// With Search and Multiple Selection
<script setup lang="ts">
import { ref } from 'vue';
import { SeoSelectSearch } from 'seo-select/vue';
const selectRef = ref();
const handleReset = () => selectRef.value?.reset();
</script>
<template>
<SeoSelectSearch
ref="selectRef"
name="city"
multiple
showReset
:optionItems="[
{ value: 'seoul', label: 'Seoul' },
{ value: 'tokyo', label: 'Tokyo' }
]"
@select="(detail) => console.log('Selected:', detail)"
@searchChange="(detail) => console.log('Search:', detail.searchText)"
/>
</template>
Angular Wrapper
Use the official Angular wrapper as standalone components:
import { Component } from '@angular/core';
import { SeoSelectComponent, SeoSelectSearchComponent } from 'seo-select/angular';
import 'seo-select/styles';
@Component({
selector: 'app-select-demo',
standalone: true,
imports: [SeoSelectComponent, SeoSelectSearchComponent],
template: `
<app-seo-select
name="framework"
theme="float"
[optionItems]="options"
(selectEvent)="onSelect($event)"
(resetEvent)="onReset($event)"
/>
`
})
export class SelectDemoComponent {
options = [
{ value: 'angular', label: 'Angular' },
{ value: 'ionic', label: 'Ionic' },
{ value: 'ngrx', label: 'NgRx' }
];
onSelect(detail: { label: string; value: string }) {
console.log('Selected:', detail);
}
onReset(detail: any) {
console.log('Reset:', detail);
}
}
// With Search
@Component({
selector: 'app-search-demo',
standalone: true,
imports: [SeoSelectSearchComponent],
template: `
<app-seo-select-search
name="city"
[multiple]="true"
[showReset]="true"
[optionItems]="options"
(selectEvent)="onSelect($event)"
(searchChangeEvent)="onSearchChange($event)"
/>
`
})
export class SearchDemoComponent {
// ...
}
Solid.js Wrapper
Use the official Solid.js wrapper with reactive primitives:
import { SeoSelect, SeoSelectSearch } from 'seo-select/solid';
import 'seo-select/styles';
export default function MyComponent() {
const options = [
{ value: 'solid', label: 'Solid.js' },
{ value: 'start', label: 'SolidStart' }
];
return (
<SeoSelect
name="framework"
theme="float"
optionItems={options}
onSelect={(e) => console.log('Selected:', e.detail)}
/>
);
}
// With Ref
export default function SearchExample() {
let selectEl;
const handleReset = () => {
selectEl?.reset?.();
};
return (
<>
<SeoSelectSearch
ref={(el) => { selectEl = el; }}
name="city"
multiple
optionItems={[
{ value: 'seoul', label: 'Seoul' },
{ value: 'tokyo', label: 'Tokyo' }
]}
onSelect={(e) => console.log('Selected:', e.detail)}
onSearchChange={(e) => console.log('Search:', e.detail.searchText)}
/>
<button onClick={handleReset}>Reset</button>
</>
);
}
Qwik Wrapper
Use the official Qwik wrapper with QRL event handlers:
import { component$ } from '@builder.io/qwik';
import { SeoSelect, SeoSelectSearch } from 'seo-select/qwik';
import 'seo-select/styles';
export const SelectDemo = component$(() => {
const options = [
{ value: 'qwik', label: 'Qwik' },
{ value: 'qwikcity', label: 'QwikCity' }
];
return (
<SeoSelect
name="framework"
theme="float"
optionItems={options}
onSelect$={(e) => console.log('Selected:', e.detail)}
/>
);
});
// With Search
export const SearchDemo = component$(() => {
return (
<SeoSelectSearch
name="city"
multiple
showReset
optionItems={[
{ value: 'seoul', label: 'Seoul' },
{ value: 'tokyo', label: 'Tokyo' }
]}
onSelect$={(e) => console.log('Selected:', e.detail)}
onSearchChange$={(e) => console.log('Search:', e.detail.searchText)}
/>
);
});
Vanilla JavaScript / Web Component
Use the web component directly for vanilla JavaScript or unsupported frameworks:
import 'seo-select/types';
import 'seo-select/styles';
import 'seo-select/components/seo-select-search';
const select = document.createElement('seo-select-search');
select.optionItems = [
{ value: 'vanilla', label: 'Vanilla JS' },
{ value: 'typescript', label: 'TypeScript' }
];
select.theme = 'float';
select.multiple = true;
select.addEventListener('onSelect', (event) => {
console.log('Selected:', event.detail);
});
select.addEventListener('onSearchChange', (event) => {
console.log('Search:', event.detail.searchText);
});
document.body.appendChild(select);
Troubleshooting
Common Issues & Solutions
Solutions for common issues you might encounter when using SEO Select components in your projects.
CSS Style Conflicts
SEO Select uses Light DOM (no Shadow DOM) for maximum CSS compatibility. If you experience style conflicts with your existing CSS, try these solutions:
Option 1: CSS Scoping with Wrapper Class
/* Scope your styles to avoid conflicts */
.my-app-container .seo-select {
/* Your custom styles */
}
/* Or use more specific selectors */
.my-form seo-select .select-trigger {
border-color: #your-color;
}
Option 2: Use CSS Custom Properties
/* Override using CSS variables instead of direct selectors */
seo-select {
--select-font-size: 14px;
--primary-color: #your-brand-color;
--select-border-radius: 8px;
--select-background: #ffffff;
}
/* Dark mode overrides */
seo-select[dark] {
--select-background: #1a1a2e;
--select-text-color: #ffffff;
}
SPA Memory Management
SEO Select properly cleans up resources when components are removed from the DOM. For Single Page Applications (React, Vue, Next.js, Nuxt, etc.):
- Automatic Cleanup: Event listeners are automatically removed in
disconnectedCallback - Virtual Scroll: Virtual scroll instances are properly destroyed
- DOM References: All DOM references are released to prevent memory leaks
- Static Instances: Static instance references are nullified
React: Proper Cleanup Pattern
// React useEffect cleanup ensures proper unmounting
useEffect(() => {
const element = selectRef.current;
if (!element) return;
const handleSelect = (e) => console.log(e.detail);
element.addEventListener('onSelect', handleSelect);
// Cleanup function - runs when component unmounts
return () => {
element.removeEventListener('onSelect', handleSelect);
// Component's disconnectedCallback handles internal cleanup
};
}, []);
Vue: Proper Cleanup Pattern
// Vue Composition API cleanup
import { ref, onMounted, onBeforeUnmount } from 'vue';
const selectRef = ref(null);
let handleSelect = null;
onMounted(() => {
handleSelect = (e) => console.log(e.detail);
selectRef.value?.addEventListener('onSelect', handleSelect);
});
onBeforeUnmount(() => {
selectRef.value?.removeEventListener('onSelect', handleSelect);
// Component's disconnectedCallback handles internal cleanup
});
SSR Hydration Issues
When using SEO Select with SSR frameworks (Next.js, Nuxt, SvelteKit), you may encounter hydration mismatches. Here's how to handle them:
Next.js: Dynamic Import Solution
'use client';
import dynamic from 'next/dynamic';
import { useEffect, useState } from 'react';
// Disable SSR for the component
const SelectWrapper = dynamic(
() => import('./SelectWrapper'),
{ ssr: false }
);
export default function Page() {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) return <div>Loading...</div>;
return <SelectWrapper />;
}
Nuxt 3: ClientOnly Wrapper
<template>
<ClientOnly>
<seo-select name="example" width="200px">
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</seo-select>
<template #fallback>
<div>Loading...</div>
</template>
</ClientOnly>
</template>
React Event Binding
React's synthetic event system does not automatically bind to Web Component custom events. You must use ref with addEventListener:
Incorrect (Won't Work)
// This will NOT work - React doesn't bind custom events
<seo-select
onSelect={(e) => console.log(e.detail)} // Won't fire!
onReset={(e) => console.log(e.detail)} // Won't fire!
/>
Correct (Use ref + addEventListener)
const selectRef = useRef(null);
useEffect(() => {
const el = selectRef.current;
if (!el) return;
const handleSelect = (e) => console.log(e.detail);
el.addEventListener('onSelect', handleSelect);
return () => el.removeEventListener('onSelect', handleSelect);
}, []);
return <seo-select ref={selectRef} name="example" />;