Smart autocomplete (38kB) that learns from user input and works perfectly with React controlled components
Traditional autocomplete breaks with React's controlled components. GhostComplete provides a reliable solution that learns common words.
// â Often breaks with controlled components
function SearchInput() {
const [value, setValue] = useState('');
return (
<input
value={value}
onChange={(e) => setValue(e.target.value)}
autoComplete="on" // Unreliable
/>
);
}
Native autocomplete often fails with React's controlled inputs due to state management conflicts.
// â
Works perfectly with controlled components
import 'ghostcomplete'; // Auto-initializes and exposes GhostComplete globally
function SearchInput() {
const [value, setValue] = useState('');
const inputRef = useGhostComplete('search');
return (
<input
ref={inputRef}
value={value}
onChange={(e) => setValue(e.target.value)}
data-autocomplete="search"
/>
);
}
GhostComplete seamlessly integrates with React's controlled components and learns common words from user input.
Try typing in the inputs below. The library shows suggestions from the first character and learns words (3+ chars) for future use.
How it works: Suggestions appear from the first character you type. Words with 3+ characters are saved to build vocabulary for each input group.
Built specifically for modern web applications with word learning capabilities
Shows suggestions from the first character typed, learns words (3+ chars) to build smart vocabulary and provide increasingly relevant completions.
Designed specifically for React controlled components where native autocomplete often fails.
Organize completions by context - search queries, comments, forms - each with their own vocabulary.
Provides word suggestions based on typing frequency and context within each group.
Only 38kB package size with zero dependencies. Fast loading and minimal impact on your bundle size.
Efficient trie-based search with intelligent debouncing and memory management.
npm install ghostcomplete
<script src="https://unpkg.com/ghostcomplete@latest/dist/index.umd.js"></script>
<!-- â
Automatic - Just add data-autocomplete attribute -->
<input type="text" data-autocomplete="search" placeholder="Start typing...">
<textarea data-autocomplete="comments" placeholder="Write comment..."></textarea>
<script>
// Import the library (automatically detects and initializes inputs)
import 'ghostcomplete';
// That's it! Elements with data-autocomplete work automatically
// â ī¸ Manual initialization only needed for:
// - Dynamic elements created after page load
// - React components (useEffect hooks)
// - Elements without data-autocomplete attribute
GhostComplete.init('#my-input', 'search'); // For specific elements
GhostComplete.initAll('default'); // For all elements at once
// Configure groups as needed
GhostComplete.setGroupConfig('search', {
MAX_SUGGESTIONS: 8,
DEBOUNCE_DELAY: 100
});
</script>
GhostComplete works automatically when:
data-autocomplete
attribute
<!-- Just works automatically! -->
<input data-autocomplete="search" placeholder="Search...">
<textarea data-autocomplete="comments"></textarea>
You need to call init() when:
data-autocomplete
// Dynamic elements
const input = document.createElement('input');
GhostComplete.init(input, 'search');
// React components
useEffect(() => {
GhostComplete.init(inputRef.current, 'search');
}, []);
Start with the automatic data-autocomplete
approach.
Only use manual init()
when you need it
for dynamic content or framework components.
import { useEffect, useRef } from 'react';
// Import GhostComplete (auto-initializes)
import 'ghostcomplete';
function useGhostComplete(group = 'default', config = {}) {
const ref = useRef(null);
useEffect(() => {
if (ref.current) {
// Apply configuration
if (Object.keys(config).length > 0) {
GhostComplete.setGroupConfig(group, config);
}
// Manual init required for React components
GhostComplete.init(ref.current, group);
}
}, [group]);
return ref;
}
// Usage
function SearchInput() {
const searchRef = useGhostComplete('search', {
MAX_SUGGESTIONS: 8,
DEBOUNCE_DELAY: 100
});
return (
<input
ref={searchRef}
data-autocomplete="search"
placeholder="Search..."
/>
);
}
// Note: For static HTML, no init() needed:
// <input data-autocomplete="search" placeholder="Search..." />
function ContactForm() {
const nameRef = useGhostComplete('names');
const companyRef = useGhostComplete('companies');
const messageRef = useGhostComplete('messages', {
MAX_WORDS: 500
});
return (
<form>
<input
ref={nameRef}
data-autocomplete="names"
placeholder="Your name"
/>
<input
ref={companyRef}
data-autocomplete="companies"
placeholder="Company name"
/>
<textarea
ref={messageRef}
data-autocomplete="messages"
placeholder="Your message"
/>
</form>
);
}
function DynamicSearch({ category }) {
const searchRef = useRef(null);
const [query, setQuery] = useState('');
useEffect(() => {
if (searchRef.current) {
// Configure different settings based on category
const configs = {
products: { MAX_SUGGESTIONS: 10, MAX_WORDS: 1000 },
users: { MAX_SUGGESTIONS: 8, MAX_WORDS: 500 },
tags: { MAX_SUGGESTIONS: 6, MAX_WORDS: 300 }
};
const groupName = `search-${category}`;
GhostComplete.setGroupConfig(groupName, configs[category] || {});
GhostComplete.init(searchRef.current, groupName);
}
}, [category]);
return (
<input
ref={searchRef}
value={query}
onChange={(e) => setQuery(e.target.value)}
data-autocomplete={`search-${category}`}
placeholder={`Search ${category}...`}
/>
);
}
Method | Parameters | Description |
---|---|---|
GhostComplete.init(element, group) |
element: HTMLElement, group: string | Initialize autocomplete on a specific element |
GhostComplete.initAll(group) |
group: string (optional) | Initialize all elements with data-autocomplete attribute |
GhostComplete.setGroupConfig(group, config, classes) |
group: string, config: object, classes: object | Configure settings and styling for a specific group |
GhostComplete.getGroupConfig(group) |
group: string | Get current configuration for a group |
Method | Parameters | Description |
---|---|---|
GhostComplete.clearWords(group) |
group: string | Clear learned words for a specific group |
GhostComplete.clearAll(group) |
group: string | Clear all data for a group |
GhostComplete.getStats(group) |
group: string | Get statistics about learned data for a group |
Option | Default | Description |
---|---|---|
MAX_WORDS |
300 | Maximum words to store per group |
MAX_SUGGESTIONS |
5 | Maximum suggestions to show |
MAX_STABLE |
10 | Stable words that won't be removed |
Option | Default | Description |
---|---|---|
DEBOUNCE_DELAY |
160ms | Delay before processing input |
STORAGE_SYNC_DELAY |
600ms | Delay before saving to localStorage |
IDLE_CLEANUP_DELAY |
2000ms | Delay before cleaning unused data |
Class Property | Target Element |
---|---|
popupContainer |
Suggestions popup container |
popupRow |
Individual suggestion rows |
popupRowSelected |
Currently selected suggestion |
popupHint |
Hint text in popup |
GhostComplete.setGroupConfig('search', {
MAX_SUGGESTIONS: 8,
DEBOUNCE_DELAY: 100,
MAX_WORDS: 500
}, {
popupContainer: 'my-search-popup',
popupRowSelected: 'active-suggestion',
popupHint: 'search-hint-text'
});