SEO Select

JavaScript React Vue Angular Solid Qwik TypeScript

Discover a new kind of lightweight and modern select component intuitive search, multilingual support, and fully accessible by design.

🌏 Welcome

Universal Framework Support

SEO Select is built as a Web Component with global TypeScript extensions, making it compatible with all modern frameworks out of the box.

React
seo-select/react
Vue
seo-select/vue
Angular
seo-select/angular
Solid.js
seo-select/solid
Qwik
seo-select/qwik

🎯 Zero Configuration

Single import enables type safety across all frameworks

import 'seo-select/types';

🔒 Type Safety

IntelliSense and auto-completion in every environment

onSelect={(e) => console.log(e.detail)}

⚡ Future Proof

Built on web standards, works everywhere

<seo-select theme="float" />

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';
📦 Single import enables global component availability across your entire application

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>
🏷️ Standard HTML syntax with enhanced attributes and automatic TypeScript IntelliSense

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();
⚙️ Rich JavaScript API for dynamic option management and programmatic control

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);
});
📡 Rich event system supporting both custom component events and standard form events

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>
💡 Uses standard HTML option tags with selected attribute support

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';
🔧 Populated via JavaScript optionItems property - ideal for dynamic content

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.

⌨️ Try Tab, Tab + Shift, Arrow keys, Enter, Escape - fully accessible navigation

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.

📊 Watch console and event log above for detailed interaction tracking

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.

Modern floating design with smooth animations

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.

📋 Clean minimal design for professional interfaces

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.

🌙 Optimized dark theme with proper contrast ratios

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.

🔍 Dark search variant with enhanced multilingual text support

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.

🎨 Real-time theme switching with smooth transitions

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.

🏷️ Visual tag system with individual item removal and batch 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.

🌏 Multilingual search combined with tag-based multiple selection

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.

🌙 Dark theme optimized for multi-selection interfaces

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.

🔄 Batch option replacement with optional selection preservation

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.

📝 Complete form integration with validation and error handling

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;
}
💡 CSS Custom Properties provide the cleanest way to customize styles without conflicts

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
});
🧹 If you notice memory leaks, ensure components are properly unmounted when navigating between routes

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>
🌐 Web Components require browser APIs, so SSR frameworks need client-only rendering

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" />;
⚛️ This is a React limitation with Web Components, not an SEO Select issue

Logo

Loading...