Skip to main content
RapidDev - Software Development Agency
firebase-tutorial

How to Make Firestore Queries Case-Insensitive

Firestore queries are case-sensitive by default. To make them case-insensitive, store a lowercase copy of each searchable field (for example, name_lowercase) and query against that normalized field. Use a Cloud Function or client-side logic to auto-generate the lowercase field on every write, ensuring consistent search results regardless of how users type their queries.

What you'll learn

  • Why Firestore queries are case-sensitive and how to work around it
  • How to store normalized lowercase fields for case-insensitive search
  • How to write a Cloud Function that auto-generates lowercase fields on write
  • How to query the normalized field for case-insensitive matching
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate7 min read15-20 minFirebase JS SDK v9+, Cloud Firestore, Cloud Functions v2March 2026RapidDev Engineering Team
TL;DR

Firestore queries are case-sensitive by default. To make them case-insensitive, store a lowercase copy of each searchable field (for example, name_lowercase) and query against that normalized field. Use a Cloud Function or client-side logic to auto-generate the lowercase field on every write, ensuring consistent search results regardless of how users type their queries.

Making Firestore Queries Case-Insensitive

Firestore does not support case-insensitive queries natively. A query for 'John' will not match 'john' or 'JOHN'. The standard solution is to store a lowercase copy of each searchable text field and query against that normalized field. This tutorial shows you how to create the lowercase field, automate it with a Cloud Function trigger, batch-update existing documents, and query the normalized field for consistent case-insensitive results.

Prerequisites

  • A Firebase project with Firestore enabled
  • Firebase JS SDK v9+ installed in your project
  • Node.js 18+ for Cloud Functions (optional but recommended)
  • Basic understanding of Firestore document structure

Step-by-step guide

1

Store a lowercase copy of the searchable field

When writing a document to Firestore, add an extra field that contains the lowercase version of your searchable text. For example, if you have a name field, also store name_lowercase. This keeps the original casing for display while providing a normalized version for queries. Apply this pattern to every field you want to search case-insensitively.

typescript
1import { getFirestore, collection, addDoc, serverTimestamp } from 'firebase/firestore';
2
3const db = getFirestore();
4
5async function addUser(name: string, email: string) {
6 const docRef = await addDoc(collection(db, 'users'), {
7 name: name,
8 name_lowercase: name.toLowerCase(),
9 email: email,
10 email_lowercase: email.toLowerCase(),
11 createdAt: serverTimestamp()
12 });
13 console.log('User added with ID:', docRef.id);
14 return docRef;
15}

Expected result: Each new document has both the original field and a lowercase copy stored alongside it.

2

Query using the lowercase field

When searching, convert the user's input to lowercase and query the normalized field instead of the original. This ensures that searching for 'john', 'John', or 'JOHN' all return the same results. For prefix searches (starts with), use the >= and < operators with the lowercase input.

typescript
1import { getFirestore, collection, query, where, getDocs } from 'firebase/firestore';
2
3const db = getFirestore();
4
5async function searchUsers(searchTerm: string) {
6 const normalizedTerm = searchTerm.toLowerCase();
7
8 // Exact match
9 const exactQuery = query(
10 collection(db, 'users'),
11 where('name_lowercase', '==', normalizedTerm)
12 );
13
14 // Prefix search (starts with)
15 const prefixQuery = query(
16 collection(db, 'users'),
17 where('name_lowercase', '>=', normalizedTerm),
18 where('name_lowercase', '<=', normalizedTerm + '\uf8ff')
19 );
20
21 const snapshot = await getDocs(prefixQuery);
22 return snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
23}

Expected result: Queries return matching documents regardless of the original casing of the stored data.

3

Automate lowercase field generation with a Cloud Function

To avoid manually adding lowercase fields in every write operation, create a Cloud Function that triggers on document creation and update. The function reads the original fields, generates lowercase versions, and writes them back to the document. This ensures every document has normalized fields even if written from different clients or the Firebase Console.

typescript
1import { onDocumentWritten } from 'firebase-functions/v2/firestore';
2import { getFirestore } from 'firebase-admin/firestore';
3import { initializeApp } from 'firebase-admin/app';
4
5initializeApp();
6const db = getFirestore();
7
8export const normalizeUserFields = onDocumentWritten(
9 'users/{userId}',
10 async (event) => {
11 const after = event.data?.after?.data();
12 if (!after) return; // Document was deleted
13
14 const updates: Record<string, string> = {};
15
16 if (after.name && after.name_lowercase !== after.name.toLowerCase()) {
17 updates.name_lowercase = after.name.toLowerCase();
18 }
19 if (after.email && after.email_lowercase !== after.email.toLowerCase()) {
20 updates.email_lowercase = after.email.toLowerCase();
21 }
22
23 if (Object.keys(updates).length > 0) {
24 await event.data?.after?.ref.update(updates);
25 }
26 }
27);

Expected result: Every time a user document is created or updated, the Cloud Function automatically generates or updates the lowercase fields.

4

Batch-update existing documents with lowercase fields

If you already have documents without lowercase fields, write a one-time migration script using the Firebase Admin SDK. This script reads all documents in a collection, generates the lowercase values, and writes them back using batch writes for efficiency. The 500-operation limit per batch means you need to commit and create new batches for large collections.

typescript
1import { initializeApp, cert } from 'firebase-admin/app';
2import { getFirestore } from 'firebase-admin/firestore';
3
4initializeApp();
5const db = getFirestore();
6
7async function migrateToLowercase() {
8 const usersRef = db.collection('users');
9 const snapshot = await usersRef.get();
10 let batch = db.batch();
11 let count = 0;
12
13 for (const doc of snapshot.docs) {
14 const data = doc.data();
15 const updates: Record<string, string> = {};
16
17 if (data.name) updates.name_lowercase = data.name.toLowerCase();
18 if (data.email) updates.email_lowercase = data.email.toLowerCase();
19
20 if (Object.keys(updates).length > 0) {
21 batch.update(doc.ref, updates);
22 count++;
23 }
24
25 if (count >= 499) {
26 await batch.commit();
27 batch = db.batch();
28 count = 0;
29 }
30 }
31
32 if (count > 0) await batch.commit();
33 console.log('Migration complete');
34}
35
36migrateToLowercase();

Expected result: All existing documents now have lowercase fields and are searchable case-insensitively.

5

Update Firestore security rules for the new fields

Add security rules that protect the lowercase fields from being set to incorrect values by clients. The rules should validate that the lowercase field matches the lowercase version of the original field. This prevents clients from setting arbitrary values in the normalized fields, which would break search consistency.

typescript
1rules_version = '2';
2service cloud.firestore {
3 match /databases/{database}/documents {
4 match /users/{userId} {
5 allow read: if request.auth != null;
6 allow create, update: if request.auth != null
7 && request.resource.data.name is string
8 && request.resource.data.name_lowercase == request.resource.data.name.lower();
9 }
10 }
11}

Expected result: Security rules reject writes where the lowercase field does not match the lowercased original field.

Complete working example

case-insensitive-search.ts
1import {
2 getFirestore,
3 collection,
4 addDoc,
5 query,
6 where,
7 getDocs,
8 serverTimestamp
9} from 'firebase/firestore';
10import { initializeApp } from 'firebase/app';
11
12const firebaseConfig = {
13 apiKey: 'YOUR_API_KEY',
14 authDomain: 'YOUR_PROJECT.firebaseapp.com',
15 projectId: 'YOUR_PROJECT_ID',
16 storageBucket: 'YOUR_PROJECT.appspot.com',
17 messagingSenderId: 'YOUR_SENDER_ID',
18 appId: 'YOUR_APP_ID'
19};
20
21const app = initializeApp(firebaseConfig);
22const db = getFirestore(app);
23
24// Add a document with normalized lowercase fields
25export async function addUser(name: string, email: string) {
26 return addDoc(collection(db, 'users'), {
27 name,
28 name_lowercase: name.toLowerCase(),
29 email,
30 email_lowercase: email.toLowerCase(),
31 createdAt: serverTimestamp()
32 });
33}
34
35// Case-insensitive exact search
36export async function findUserByName(searchName: string) {
37 const q = query(
38 collection(db, 'users'),
39 where('name_lowercase', '==', searchName.toLowerCase())
40 );
41 const snapshot = await getDocs(q);
42 return snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
43}
44
45// Case-insensitive prefix search
46export async function searchUsersByPrefix(prefix: string) {
47 const normalizedPrefix = prefix.toLowerCase();
48 const q = query(
49 collection(db, 'users'),
50 where('name_lowercase', '>=', normalizedPrefix),
51 where('name_lowercase', '<=', normalizedPrefix + '\uf8ff')
52 );
53 const snapshot = await getDocs(q);
54 return snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
55}

Common mistakes when making Firestore Queries Case-Insensitive

Why it's a problem: Querying the original field instead of the lowercase field

How to avoid: Always query the _lowercase version of the field. Update your search functions to use where('name_lowercase', '==', input.toLowerCase()) instead of where('name', '==', input).

Why it's a problem: Forgetting to update the lowercase field when the original field changes

How to avoid: Use a Cloud Function trigger on onDocumentWritten to automatically regenerate lowercase fields on every write, or ensure all client-side update code includes the lowercase field.

Why it's a problem: Creating an infinite loop in the Cloud Function that normalizes fields

How to avoid: Always check if the lowercase field already matches before writing. Compare after.name_lowercase !== after.name.toLowerCase() to avoid triggering the function again on its own update.

Best practices

  • Use a consistent naming convention like fieldName_lowercase for all normalized fields
  • Automate lowercase field generation with Cloud Functions instead of relying on client-side code
  • Validate lowercase fields in Firestore security rules using the .lower() string method
  • Run a one-time batch migration to add lowercase fields to existing documents
  • Index the lowercase fields for query performance, especially for prefix searches
  • Consider storing additional normalized versions (trimmed, accent-stripped) for international text
  • Document the normalization pattern in your project so all developers follow the same approach
  • For full-text search beyond prefix matching, consider integrating Algolia or Typesense

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I have a Firestore collection with a name field and I need case-insensitive search. Show me how to store a lowercase copy of the field, query it, and write a Cloud Function that automatically generates the lowercase field on every write.

Firebase Prompt

Write a Firebase Cloud Function v2 that triggers on document writes in a users collection and automatically generates lowercase versions of the name and email fields. Include the security rules that validate the lowercase fields match.

Frequently asked questions

Does Firestore support case-insensitive queries natively?

No. Firestore queries are strictly case-sensitive. There is no built-in operator or query modifier for case-insensitive matching. The standard workaround is storing a lowercase copy of each searchable field and querying that instead.

Will storing lowercase fields increase my Firestore costs?

Marginally. Each additional field adds a small amount of storage and an automatic single-field index. For most applications, this overhead is negligible compared to the benefit of reliable search.

Can I use Firestore security rules to enforce lowercase field consistency?

Yes. Firestore rules support the .lower() method on strings. You can add a rule like request.resource.data.name_lowercase == request.resource.data.name.lower() to ensure clients always write correct lowercase values.

What about case-insensitive search for non-Latin characters?

The toLowerCase() JavaScript method handles most Unicode characters correctly, including accented Latin characters and Cyrillic. For languages with complex case rules (like Turkish dotted/dotless i), use toLocaleLowerCase('tr') with the appropriate locale.

Should I use a Cloud Function or handle lowercase on the client?

A Cloud Function is more reliable because it catches writes from all sources including the Firebase Console, Admin SDK, and different client apps. Client-side normalization works for simple setups but risks inconsistency if any write path skips the normalization.

How do I handle case-insensitive search across multiple fields?

Create a lowercase copy for each searchable field. For combined search, consider a search_keywords array containing lowercase tokens from all searchable fields, then use array-contains queries against it.

Can RapidDev help implement case-insensitive search in my Firebase app?

Yes. RapidDev can set up the full case-insensitive search pattern for your Firebase project, including Cloud Functions for automated normalization, batch migration of existing data, and integration with external search services like Algolia for more advanced search needs.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.