When building search functionality in a Vue application, implementing persistant search history improves user experience by allowing users to quickly access their recent queries across sessions.
Core Implementation Strategy
The approach involves three key principles:
- Store search history data in browser localStorage for permanent persistence
- Load cached data into component state during initialization
- Synchronize all modifications (additions and deletions) to the localStorage cache
Cmoponent Template
<template>
<div class="search-container">
<div v-if="displayHistory" class="history-panel">
<div class="header-row">
<h4>Recent Searches</h4>
<button @click="removeAllHistory" class="delete-btn">
<img src="/assets/trash-icon.png" alt="Clear" width="16"/>
</button>
</div>
<div class="history-items">
<span
v-for="(keyword, idx) in recentSearches"
:key="idx"
@click="executeSearch(keyword)"
class="keyword-tag">
{{ keyword }}
</span>
</div>
</div>
</div>
</template>
Component State Definition
<script>
export default {
name: 'SearchHistory',
data() {
return {
recentSearches: [],
displayHistory: false,
isLoading: false
}
},
mounted() {
this.loadPersistedHistory()
},
methods: {
// Restore search history from localStorage
loadPersistedHistory() {
const cached = localStorage.getItem('userSearchQueries')
if (cached) {
try {
this.recentSearches = JSON.parse(cached)
this.displayHistory = this.recentSearches.length > 0
} catch (e) {
this.recentSearches = []
}
}
},
// Persist current history to localStorage
persistHistory() {
localStorage.setItem('userSearchQueries', JSON.stringify(this.recentSearches))
},
// Clear all search history
removeAllHistory() {
localStorage.removeItem('userSearchQueries')
this.recentSearches = []
this.displayHistory = false
},
// Add new search term to history
addSearchTerm(term) {
// Skip empty searches
if (!term || !term.trim()) return
const trimmedTerm = term.trim()
// Remove existing duplicate if present
const existingIndex = this.recentSearches.indexOf(trimmedTerm)
if (existingIndex !== -1) {
this.recentSearches.splice(existingIndex, 1)
}
// Insert at beginning
this.recentSearches.unshift(trimmedTerm)
// Maintain maximum of 10 items
if (this.recentSearches.length > 10) {
this.recentSearches.pop()
}
this.persistHistory()
},
// Execute search using historical keyword
executeSearch(keyword) {
this.performSearch(keyword).then(results => {
this.searchResults = results
this.displayHistory = false
})
},
async performSearch(query) {
this.isLoading = true
try {
const response = await searchApi.fetchResults(query)
return response.data.items
} finally {
this.isLoading = false
}
}
}
}
</script>
Integration Example
To use this component with your search form, invoke the addSearchTerm method when a search is submitted:
// In your search form component
handleSearchSubmit(query) {
// Add to history before executing search
this.$refs.historyComponent.addSearchTerm(query)
// Proceed with search execution
this.performSearch(query)
}
Key Features
- Duplicate Prevention: If a search term already exists in history, it moves to the top instead of creating duplicates
- Storage Limit: History is capped at 10 items to prevent unbounded localStorage growth
- Empty Value Handling: Whitespace-only inputs are filtered out
- Error Resilience: JSON parsing failures during restore are handled gracefully
This implementation provides a seamless experience where users can quickly retry previous searches while maintaining privacy through browser-based storage that persists until explicitly cleared.