Building modern search experiences shouldn't be rocket science. Let me show you how to create a blazing-fast, real-time search system using TypeScript, Hono, and React.
The Problem
Ever tried implementing search functionality and ended up with a sluggish, overcomplicated mess? You're not alone. Most developers either:
- Over-engineer with complex search engines like Elasticsearch
- Under-deliver with basic string matching that feels broken
- Get lost in the complexity of fuzzy search algorithms
What if I told you there's a sweet spot? A TypeScript-first solution that's both powerful and simple?
Meet search-plus-ts
search-plus-ts
is a lightweight, TypeScript-native search library that brings intelligent search capabilities without the overhead. Today, I'll walk you through building a complete full-stack search application using:
- Backend: Hono (the fast, lightweight web framework)
- Frontend: React with Vite
- Search Engine: search-plus-ts
- Styling: Tailwind CSS
🎯 What We're Building
A real-time search interface for blog posts that features:
- ⚡ Instant search results as you type
- 🎨 Highlighted search matches
- 🔍 Multi-field searching (title, category, date)
- 📱 Responsive design
- 🚀 TypeScript throughout
Setting Up the Backend
Let's start with our Hono server. First, the data structure:
// hono-server/src/data.ts
const posts = [
{
title: 'Copyleaks Study Finds Explosive Growth of AI Content on the Web',
categorie: 'Artificial Intelligence',
date: 'May 1st 2024, 5:00:46 am',
},
{
title: 'Musk\'s YouTube-Like TV Venture Faces Strong Challenges From Incumbents',
categorie: 'Online Entertainment',
date: 'March 12th 2024, 5:00:59 am',
},
// ... more posts
]
export default posts
Now, the server implementation:
// hono-server/src/index.ts
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
import SearchPlusTs from 'search-plus-ts'
import posts from './data.js'
import { cors } from 'hono/cors'
const app = new Hono()
app.use(cors())
// Get all posts
app.get('/', (c) => {
return c.json({ posts })
})
// Search endpoint - this is where the magic happens
app.get('/search', (c) => {
const search = c.req.query().q
if (!search) {
return c.json({ posts: [] })
}
const SearchPlus = new SearchPlusTs({
data: posts,
keys: ['title', 'categorie', 'date'], // Fields to search
})
const results = SearchPlus.search(search)
return c.json({ results })
})
serve({ fetch: app.fetch, port: 3000 }, (info) => {
console.log(`Server is running on http://localhost:${info.port}`)
})
Building the React Frontend
The frontend is where users interact with our search. Here's the complete component:
// vite-react-client/src/App.tsx
import { useEffect, useState } from 'react'
import { highlightWordsTag } from 'search-plus-ts'
const App: React.FC = () => {
const [query, setQuery] = useState('')
const [posts, setPosts] = useState<{
title: string
categorie: string
date: string
}[]>([])
useEffect(() => {
if (query.trim() !== '') {
fetch('http://localhost:3000/search?q=' + query)
.then((res) => res.json())
.then((data) => setPosts(data.results))
} else {
fetch('http://localhost:3000')
.then((res) => res.json())
.then((data) => setPosts(data.posts))
}
}, [query])
return (
<div className='max-w-3xl mx-auto p-6'>
<input
type='search'
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder='Search posts...'
className='w-full p-3 border border-stone-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-orange-500 mb-6 shadow-sm transition-all'
/>
{posts.length === 0 ? (
<p className='text-center text-stone-500 mt-10'>No results found.</p>
) : (
posts.map((post, id) => (
<div
key={id}
className='mb-6 p-6 bg-white hover:bg-stone-50 transition-colors duration-200 border border-stone-200 rounded-2xl shadow-sm'
>
<div className='text-xl font-bold text-stone-800 mb-2'>
<span className='text-orange-600'>Title:</span>{' '}
<span
dangerouslySetInnerHTML={{
__html: highlightWordsTag(post.title, query, 'search-match'),
}}
/>
</div>
<div className='flex justify-between items-center'>
<div className='text-sm text-stone-600'>
<span className='font-semibold text-orange-500'>Category:</span>{' '}
<span
dangerouslySetInnerHTML={{
__html: highlightWordsTag(post.categorie, query, 'search-match'),
}}
/>
</div>
<div className='text-sm text-stone-600'>
<span className='font-semibold text-orange-500'>Date:</span>{' '}
<span
dangerouslySetInnerHTML={{
__html: highlightWordsTag(post.date, query, 'search-match'),
}}
/>
</div>
</div>
</div>
))
)}
</div>
)
}
export default App
The Secret Sauce: Highlighting Matches
Notice the highlightWordsTag
function? This is what makes search results feel professional:
/* vite-react-client/src/index.css */
@import 'tailwindcss';
.search-match {
@apply bg-yellow-300 font-semibold px-1;
}
This CSS class automatically highlights matching text in search results, creating that satisfying "Google-like" experience users expect.
Running the Application
The beauty of this setup is its simplicity. Just run:
# Clone the example
npx gitpick https://github.com/devgauravjatt/search-plus-ts/examples/with-server-and-client
cd with-server-and-client
# Install dependencies
npm install
# Start both server and client
npm run dev
The monorepo setup with workspaces means everything runs concurrently - your backend API at localhost:3000
and your React app at localhost:5173
.
Why This Architecture Works
🎯 Separation of Concerns
- Server handles search logic and data
- Client focuses purely on UI/UX
- Clean API contract between layers
⚡ Performance
- search-plus-ts is optimized for speed
- Hono is one of the fastest web frameworks
- React's virtual DOM handles UI updates efficiently
🔧 Developer Experience
- Full TypeScript support throughout
- Hot reload on both ends
- Simple, predictable data flow
📈 Scalability
- Easy to add more search fields
- Simple to extend with filters
- Straightforward to optimize further
Real-World Applications
This pattern works great for:
- Documentation sites with search
- E-commerce product filtering
- Blog platforms with post search
- Admin dashboards with data lookup
- Content management systems
Advanced Features You Can Add
Want to take this further? Consider adding:
- Debounced search to reduce API calls
- Search suggestions and autocomplete
- Filter by category or date ranges
- Pagination for large datasets
- Search analytics and popular queries
Key Takeaways
Building great search doesn't require complex infrastructure. With the right tools:
- search-plus-ts handles the intelligent matching
- Hono provides a fast, modern backend
- React delivers smooth user interactions
- TypeScript keeps everything type-safe
The result? A search experience that feels instant, looks professional, and scales with your needs.
Try It Yourself
Ready to build your own search-powered application?
What kind of search features are you planning to build? Let me know in the comments below! 👇
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.