🔍 Smart Route Search
Automatically generates search index based on application routes without manual configuration
Modern global search experience providing quick page navigation and intelligent search suggestions. Supports keyboard shortcuts, fuzzy search, and grouped results, enabling users to quickly find and navigate to any page in the application.
View Live Demo
🔍 Smart Route Search
Automatically generates search index based on application routes without manual configuration
⚡ Keyboard Shortcuts
Support for Ctrl+K / Cmd+K shortcuts to quickly open search
📂 Grouped Results
Intelligently groups results by functional modules for quick related page location
🎯 Fuzzy Matching
Supports fuzzy matching for page titles, paths, and parent menus
⌨️ Keyboard Navigation
Complete keyboard navigation support with arrow keys for selection and Enter to confirm
🌐 Internationalization
Full support for multi-language environments with localized search results
The simplest global search component integration:
<template><div> <!-- Search trigger in header layout --> <header class="flex items-center justify-between p-4 bg-white dark:bg-gray-800 shadow"> <div class="flex items-center gap-4"> <h1 class="text-xl font-semibold">My Application</h1> </div>
<!-- Global Search Component --> <GlobalSearch trigger-class="flex items-center gap-2 px-3 py-1.5 text-sm bg-gray-100 dark:bg-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors" />
<div class="flex items-center gap-3"> <!-- Other header controls --> </div> </header></div></template>
<script setup lang="ts">import GlobalSearch from '@/components/GlobalSearch/index.vue'</script>Using programmatic approach to control the search component:
<template><div> <!-- Custom search triggers --> <div class="search-controls"> <button @click="openSearch" class="search-trigger" > <Search class="w-4 h-4" /> <span>Search pages...</span> <kbd class="shortcut-key">⌘K</kbd> </button>
<!-- Additional trigger options --> <button @click="searchHelp" class="help-button" title="Search Help" > <HelpCircle class="w-4 h-4" /> </button> </div>
<!-- Global Search Component --> <GlobalSearch v-model:open="searchOpen" @search="handleSearch" @select="handleSelect" />
<!-- Search help modal --> <SearchHelpModal v-model:open="helpOpen" /></div></template>
<script setup lang="ts">import { ref } from 'vue'import { Search, HelpCircle } from 'lucide-vue-next'import GlobalSearch from '@/components/GlobalSearch/index.vue'import SearchHelpModal from './components/SearchHelpModal.vue'import type { SearchItem } from '@/hooks/useGlobalSearch'
const searchOpen = ref(false)const helpOpen = ref(false)
// Open searchconst openSearch = () => {searchOpen.value = true}
// Search helpconst searchHelp = () => {helpOpen.value = true}
// Handle search query changesconst handleSearch = (query: string) => {console.log('Search query:', query)// Analytics can be added heretrackSearchQuery(query)}
// Handle search result selectionconst handleSelect = (item: SearchItem) => {console.log('Selected item:', item)// Access analytics can be addedtrackPageAccess(item.path)}
// Analytics functions (example)const trackSearchQuery = (query: string) => {// Send search analytics dataif (query.length >= 2) { // analytics.track('global_search', { query })}}
const trackPageAccess = (path: string) => {// Record page access// analytics.track('page_access_via_search', { path })}</script>
<style>.search-trigger {@apply flex items-center gap-2 px-3 py-2 bg-gray-50 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg border border-gray-200 dark:border-gray-700 transition-colors min-w-64;}
.shortcut-key {@apply ml-auto px-1.5 py-0.5 text-xs font-mono bg-white dark:bg-gray-600 border border-gray-300 dark:border-gray-500 rounded shadow-sm;}
.help-button {@apply p-2 text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors;}</style>// composables/useCustomGlobalSearch.tsimport { ref, computed } from 'vue'import { useI18n } from 'vue-i18n'import type { SearchItem } from '@/hooks/useGlobalSearch'
export function useCustomGlobalSearch() {const { t } = useI18n()const searchQuery = ref('')
// Custom search data sourceconst customSearchItems = ref<SearchItem[]>([ // Page search ...generateRouteSearchItems(),
// Feature search { id: 'create-user', name: 'create-user', title: 'Create New User', path: '/management/user/add', icon: 'plus-circle', group: 'Quick Actions' }, { id: 'export-data', name: 'export-data', title: 'Data Export', path: '/management/user?action=export', icon: 'download', group: 'Quick Actions' },
// Help content search { id: 'help-getting-started', name: 'help-getting-started', title: 'Getting Started Guide', path: '/help/getting-started', icon: 'book-open', group: 'Help Center' }, { id: 'help-faq', name: 'help-faq', title: 'Frequently Asked Questions', path: '/help/faq', icon: 'help-circle', group: 'Help Center' },
// Settings shortcuts { id: 'settings-profile', name: 'settings-profile', title: 'Profile Settings', path: '/settings/profile', icon: 'user-circle', group: 'Settings' }, { id: 'settings-theme', name: 'settings-theme', title: 'Theme Settings', path: '/settings/appearance', icon: 'palette', group: 'Settings' }])
// Enhanced search logicconst filteredItems = computed(() => { if (!searchQuery.value.trim()) { // Show common items when no search query return customSearchItems.value.filter(item => item.group === 'Quick Actions' || item.group === 'Settings' ).slice(0, 8) }
const query = searchQuery.value.toLowerCase().trim()
return customSearchItems.value.filter(item => { // Multi-dimensional matching const matchTargets = [ item.title.toLowerCase(), item.path.toLowerCase(), item.group?.toLowerCase(), item.parent?.toLowerCase(), // Add keyword matching getItemKeywords(item).join(' ').toLowerCase() ].filter(Boolean).join(' ')
return matchTargets.includes(query) }).sort((a, b) => { // Sort by relevance score const aScore = calculateRelevanceScore(a, query) const bScore = calculateRelevanceScore(b, query) return bScore - aScore })})
// Calculate search relevance scoreconst calculateRelevanceScore = (item: SearchItem, query: string): number => { let score = 0 const title = item.title.toLowerCase()
// Exact match priority if (title === query) score += 100 // Title starts with match else if (title.startsWith(query)) score += 50 // Title contains match else if (title.includes(query)) score += 20
// Path matching if (item.path.toLowerCase().includes(query)) score += 10
// Group matching if (item.group?.toLowerCase().includes(query)) score += 5
return score}
// Get item keywordsconst getItemKeywords = (item: SearchItem): string[] => { const keywords: string[] = []
// Generate keywords based on path if (item.path.includes('/management/user')) { keywords.push('user', 'manage', 'admin', 'account') } if (item.path.includes('/management/order')) { keywords.push('order', 'manage', 'sales', 'commerce') } if (item.path.includes('/settings')) { keywords.push('setting', 'config', 'preference', 'option') } if (item.path.includes('/help')) { keywords.push('help', 'doc', 'guide', 'support') }
return keywords}
// Generate route search items (simplified example)function generateRouteSearchItems(): SearchItem[] { // This should generate from route configuration return [ { id: 'dashboard', name: 'dashboard', title: 'Dashboard', path: '/dashboard', icon: 'layout-dashboard', group: 'Main Pages' }, { id: 'user-management', name: 'user-management', title: 'User Management', path: '/management/user', icon: 'users', group: 'Management Modules' } // ... more route items ]}
return { searchQuery, searchItems: customSearchItems, filteredItems}}The built-in useGlobalSearch composable automatically generates search data from your route configuration:
Returns:
searchQuery: Ref<string> - Current search input valuesearchItems: ComputedRef<SearchItem[]> - All available search items from routesfilteredItems: ComputedRef<SearchItem[]> - Filtered items based on querygroupedItems: ComputedRef<Record<string, SearchItem[]>> - Items grouped by categoryFeatures:
Suitable for management systems requiring fast page navigation and feature access.
Perfect for CMS platforms where users need to quickly locate specific content areas and editing tools.
Ideal for complex dashboard applications with multiple modules and deep navigation structures.
// High-performance search implementationexport function useHighPerformanceSearch() {// 1. Pre-computed search indexconst searchIndex = ref<Map<string, SearchItem[]>>(new Map())
// Build search indexconst buildSearchIndex = (items: SearchItem[]) => { const index = new Map<string, SearchItem[]>()
items.forEach(item => { // Build index for each possible search term const searchTerms = [ ...item.title.toLowerCase().split(/s+/), ...item.path.toLowerCase().split(/[/-_]/), ...(item.parent?.toLowerCase().split(/s+/) || []) ]
searchTerms.forEach(term => { if (term.length >= 2) { // Only index terms with length >= 2 if (!index.has(term)) { index.set(term, []) } index.get(term)!.push(item) } }) })
searchIndex.value = index}
// 2. Fast search algorithmconst fastSearch = (query: string): SearchItem[] => { if (!query.trim()) return []
const terms = query.toLowerCase().trim().split(/s+/) let results: Set<SearchItem> = new Set()
// First term determines candidate set const firstTerm = terms[0] const candidates = searchIndex.value.get(firstTerm) || [] results = new Set(candidates)
// Subsequent terms filter results terms.slice(1).forEach(term => { const termResults = searchIndex.value.get(term) || [] const termSet = new Set(termResults)
// Intersection results = new Set([...results].filter(item => termSet.has(item))) })
return Array.from(results)}
// 3. Search cachingconst searchCache = new Map<string, SearchItem[]>()const MAX_CACHE_SIZE = 50
const getCachedResults = (query: string): SearchItem[] | null => { return searchCache.get(query) || null}
const setCachedResults = (query: string, results: SearchItem[]) => { if (searchCache.size >= MAX_CACHE_SIZE) { // LRU cleanup strategy const firstKey = searchCache.keys().next().value searchCache.delete(firstKey) } searchCache.set(query, results)}
return { buildSearchIndex, fastSearch, getCachedResults, setCachedResults}}