← Back to articles

How to Add Search to Your App (2026)

Every app with more than 50 items needs search. Users expect instant, typo-tolerant results. Here's how to implement search properly, from the simplest approach to production-grade solutions.

Options Overview

SolutionTypeBest ForCost
PostgreSQL FTSBuilt-inSimple search, existing PostgresFree
SQLite FTS5Built-inSingle-server appsFree
MeilisearchOpen-source engineSmall-medium appsFree (self-host)
TypesenseOpen-source enginePerformance-critical searchFree (self-host)
AlgoliaManaged SaaSEnterprise, no-opsFrom $0
ElasticsearchOpen-source engineComplex search, analyticsFree (self-host)
OramaJavaScript searchEdge/browser searchFree

Level 1: Database-Native Search

PostgreSQL Full-Text Search

If you're already on PostgreSQL, you have a capable search engine built in.

-- Add a search column
ALTER TABLE products ADD COLUMN search_vector tsvector;

-- Populate it
UPDATE products SET search_vector = 
  to_tsvector('english', name || ' ' || description);

-- Create an index
CREATE INDEX idx_products_search ON products USING gin(search_vector);

-- Search
SELECT * FROM products 
WHERE search_vector @@ to_tsquery('english', 'wireless & headphones')
ORDER BY ts_rank(search_vector, to_tsquery('english', 'wireless & headphones')) DESC;

Pros: No extra infrastructure. ACID-consistent with your data. Free. Cons: No typo tolerance, limited relevance tuning, no faceting, slower than dedicated engines. Best for: Apps with <100K searchable records where basic search is sufficient.

SQLite FTS5

-- Create virtual table
CREATE VIRTUAL TABLE products_fts USING fts5(name, description);

-- Insert data
INSERT INTO products_fts SELECT name, description FROM products;

-- Search
SELECT * FROM products_fts WHERE products_fts MATCH 'wireless headphones';

Pros: Zero infrastructure. Fast for its simplicity. Cons: Very basic ranking. No typo tolerance. Manual sync with main table. Best for: Small apps on SQLite that need basic text search.

Level 2: Dedicated Search Engines

Meilisearch

Meilisearch is an open-source, easy-to-use search engine designed for end-user-facing search.

// Index documents
const { MeiliSearch } = require('meilisearch');
const client = new MeiliSearch({ host: 'http://localhost:7700' });

await client.index('products').addDocuments([
  { id: 1, name: 'Wireless Headphones', category: 'Audio', price: 79 },
  { id: 2, name: 'Bluetooth Speaker', category: 'Audio', price: 49 },
]);

// Search with typo tolerance, filtering, and faceting
const results = await client.index('products').search('wireles headphnes', {
  filter: 'category = Audio AND price < 100',
  facets: ['category'],
});

Key features:

  • Typo-tolerant by default
  • Sub-50ms search responses
  • Faceted search and filtering
  • Geosearch
  • Multi-tenancy support
  • RESTful API
  • Simple deployment (single binary)

Pros: Incredibly easy to set up. Great defaults. Excellent documentation. Self-host for free. Cons: Less configurable than Elasticsearch. No distributed mode (single server). Limited analytics.

Cloud pricing: Free tier (10K documents), paid from $30/month. Self-hosted: Free. Docker or binary deployment.

Typesense

Typesense is an open-source search engine focused on performance and developer experience.

const Typesense = require('typesense');
const client = new Typesense.Client({
  nodes: [{ host: 'localhost', port: '8108', protocol: 'http' }],
  apiKey: 'xyz',
});

// Create collection with schema
await client.collections().create({
  name: 'products',
  fields: [
    { name: 'name', type: 'string' },
    { name: 'category', type: 'string', facet: true },
    { name: 'price', type: 'float' },
  ],
});

// Search
const results = await client.collections('products').documents().search({
  q: 'wireles headphnes',
  query_by: 'name',
  filter_by: 'price:<100',
  facet_by: 'category',
});

Key features:

  • Sub-millisecond search latency
  • Typo tolerance with configurable distance
  • Faceting, filtering, sorting, grouping
  • Geosearch
  • Synonyms
  • Built-in API key management and scoped keys
  • High availability (clustering)

Pros: Faster than Meilisearch in benchmarks. Clustering support. Excellent instant-search UI components. Cons: Requires schema definition upfront. Slightly more setup than Meilisearch.

Cloud pricing: Free tier (limited), paid from $30/month. Self-hosted: Free.

Algolia

Algolia is the managed search-as-a-service standard. No infrastructure to manage.

Key features:

  • Sub-10ms global search latency (CDN-backed)
  • InstantSearch UI libraries (React, Vue, Angular, vanilla JS)
  • AI-powered relevance tuning
  • A/B testing for search relevance
  • Analytics (what users search, click, convert)
  • Personalization
  • Recommend (product recommendations)
  • NeuralSearch (semantic understanding)

Pros: Best-in-class search UX out of the box. No ops. Global performance. Analytics included. Cons: Expensive at scale. Vendor lock-in. Pricing based on search requests AND records.

Pricing: Free (10K records, 10K requests/month), paid from $0 (usage-based, starts cheap but scales fast).

Elasticsearch

The original open-source search engine. Most powerful but most complex.

Best for: Log analysis, complex aggregations, and apps where you need total control over relevance scoring. Overkill for most app search. Consider OpenSearch (AWS fork) for managed hosting.

Comparison: Meilisearch vs Typesense vs Algolia

FeatureMeilisearchTypesenseAlgolia
Setup time5 min10 min15 min
Typo tolerance
Faceting
Geosearch
ClusteringManaged
AnalyticsBasicBasicExcellent
InstantSearch UI✅ (best)
Self-host
Latency<50ms<10ms<10ms
Learning curveLowLowMedium
Cost (self-host)FreeFreeN/A
Cost (cloud, 100K docs)~$30/mo~$30/mo~$50-100/mo

Implementation Guide

Step 1: Choose Your Approach

  • <10K records, simple search → PostgreSQL FTS or SQLite FTS5
  • 10K-1M records, user-facing search → Meilisearch or Typesense
  • Enterprise, no-ops, analytics needed → Algolia
  • Complex search + analytics → Elasticsearch

Step 2: Sync Your Data

Your search engine needs a copy of your data. Options:

Direct sync: After every database write, update the search index.

// After creating a product in your DB
await searchClient.index('products').addDocuments([newProduct]);

Batch sync: Periodically re-index from your database.

// Cron job: every 5 minutes
const products = await db.product.findMany({ where: { updatedAt: { gte: lastSync } } });
await searchClient.index('products').addDocuments(products);

Change Data Capture: Use database triggers or CDC tools to stream changes.

Step 3: Build the Search UI

All three major engines provide InstantSearch UI components:

// React InstantSearch with Typesense
import { InstantSearch, SearchBox, Hits, RefinementList } from 'react-instantsearch';
import TypesenseInstantSearchAdapter from 'typesense-instantsearch-adapter';

const adapter = new TypesenseInstantSearchAdapter({ /* config */ });

function SearchPage() {
  return (
    <InstantSearch searchClient={adapter.searchClient} indexName="products">
      <SearchBox />
      <RefinementList attribute="category" />
      <Hits hitComponent={ProductCard} />
    </InstantSearch>
  );
}

Step 4: Optimize Relevance

  • Searchable attributes priority: Put the most important fields first
  • Custom ranking: Sort by popularity, recency, or rating after relevance
  • Synonyms: "laptop" = "notebook", "phone" = "mobile"
  • Stop words: Ignore "the", "a", "is" in searches
  • Test with real queries: Use your analytics to see what users actually search for

Edge/Browser Search: Orama

For small datasets, search entirely in the browser with zero server calls:

import { create, insert, search } from '@orama/orama';

const db = await create({
  schema: { name: 'string', description: 'string', category: 'string' },
});

await insert(db, { name: 'Headphones', description: '...', category: 'Audio' });

const results = await search(db, { term: 'headphones', limit: 10 });

Best for: Static sites, documentation search, small product catalogs (<10K items). Zero latency, zero server costs.

FAQ

Can I use PostgreSQL full-text search for production?

Yes, for apps with basic search needs and moderate data sizes. Many successful apps use PostgreSQL FTS exclusively. Add a dedicated engine when you need typo tolerance, faceting, or sub-50ms results.

Meilisearch or Typesense?

Meilisearch for easiest setup and best defaults. Typesense for better performance and clustering. Both are excellent choices.

Is Algolia worth the cost?

If you need analytics, A/B testing, and zero ops: yes. If you can manage a search server: Meilisearch or Typesense gives you 90% of the functionality for free.

How do I keep search in sync with my database?

For most apps: update the search index in the same API handler that updates your database. For complex cases: use a message queue or CDC pipeline.

The Verdict

  • Start with PostgreSQL/SQLite FTS if your needs are basic
  • Graduate to Meilisearch or Typesense when you need instant, typo-tolerant search
  • Use Algolia when you need managed search with analytics and don't mind the cost
  • Use Orama for client-side search on static sites

For most web apps in 2026: self-hosted Meilisearch or Typesense gives you world-class search at zero cost beyond your server.

Get AI tool guides in your inbox

Weekly deep-dives on the best AI coding tools, automation platforms, and productivity software.