Firestore supports arrays as a native field type. You can store arrays when creating or updating documents, modify them atomically with arrayUnion() to add elements and arrayRemove() to remove elements without reading the document first, and query arrays using array-contains and array-contains-any operators. Arrays in Firestore are best for small, non-nested lists of primitive values like tags, categories, or user IDs.
Working with Array Fields in Cloud Firestore
Firestore arrays are useful for storing ordered lists of values like tags, category labels, participant IDs, or feature flags. Unlike traditional databases, Firestore provides atomic array operations that let you add or remove elements without reading the document first, preventing race conditions in concurrent environments. This tutorial covers creating arrays, updating them atomically, querying them, and understanding their limitations.
Prerequisites
- A Firebase project with Firestore database created
- Firebase SDK installed in your project (npm install firebase)
- Firestore initialized in your app with getFirestore()
- Basic understanding of Firestore documents and collections
Step-by-step guide
Create a document with an array field
Create a document with an array field
When creating a document with setDoc or addDoc, you can include array fields using standard JavaScript arrays. Firestore arrays can contain strings, numbers, booleans, timestamps, geopoints, references, maps, and even nested arrays (though nested arrays are not queryable). Each element maintains its position and the array preserves insertion order.
1import { getFirestore, doc, setDoc, serverTimestamp } from 'firebase/firestore';23const db = getFirestore();45// Create a document with array fields6await setDoc(doc(db, 'articles', 'article-1'), {7 title: 'Getting Started with Firebase',8 tags: ['firebase', 'tutorial', 'beginner'],9 categories: ['web-development', 'backend'],10 collaboratorIds: ['uid-alice', 'uid-bob'],11 createdAt: serverTimestamp()12});Expected result: A document is created with three array fields: tags, categories, and collaboratorIds.
Add elements to an array with arrayUnion()
Add elements to an array with arrayUnion()
Use arrayUnion() with updateDoc to add one or more elements to an existing array field without reading the document first. arrayUnion is atomic — it only adds elements that are not already in the array, so duplicate entries are automatically prevented. This is ideal for concurrent environments where multiple users might add items simultaneously.
1import { getFirestore, doc, updateDoc, arrayUnion } from 'firebase/firestore';23const db = getFirestore();45// Add new tags to an article (duplicates are ignored)6await updateDoc(doc(db, 'articles', 'article-1'), {7 tags: arrayUnion('advanced', 'cloud-functions')8});910// Add a new collaborator11await updateDoc(doc(db, 'articles', 'article-1'), {12 collaboratorIds: arrayUnion('uid-charlie')13});Expected result: The tags array now contains ['firebase', 'tutorial', 'beginner', 'advanced', 'cloud-functions']. The collaboratorIds array includes the new UID.
Remove elements from an array with arrayRemove()
Remove elements from an array with arrayRemove()
Use arrayRemove() with updateDoc to remove specific elements from an array. Like arrayUnion, this operation is atomic and does not require reading the document first. If the element does not exist in the array, the operation completes successfully without error — it is a no-op for missing elements.
1import { getFirestore, doc, updateDoc, arrayRemove } from 'firebase/firestore';23const db = getFirestore();45// Remove a tag6await updateDoc(doc(db, 'articles', 'article-1'), {7 tags: arrayRemove('beginner')8});910// Remove a collaborator11await updateDoc(doc(db, 'articles', 'article-1'), {12 collaboratorIds: arrayRemove('uid-bob')13});Expected result: The specified elements are removed from the arrays. If an element was not present, no error is thrown.
Query documents by array contents with array-contains
Query documents by array contents with array-contains
Use the array-contains query operator to find all documents where a specific value exists in an array field. This is useful for finding articles by tag, events by participant, or products by feature. Firestore automatically indexes array fields for array-contains queries. Note that you can only use one array-contains clause per query.
1import { getFirestore, collection, query, where, getDocs } from 'firebase/firestore';23const db = getFirestore();45// Find all articles tagged with 'firebase'6const q = query(7 collection(db, 'articles'),8 where('tags', 'array-contains', 'firebase')9);1011const snapshot = await getDocs(q);12snapshot.forEach((doc) => {13 console.log(doc.id, doc.data().title);14});1516// Find all articles where a specific user is a collaborator17const collabQuery = query(18 collection(db, 'articles'),19 where('collaboratorIds', 'array-contains', 'uid-alice')20);2122const collabDocs = await getDocs(collabQuery);23collabDocs.forEach((doc) => {24 console.log('Collaboration:', doc.id);25});Expected result: The query returns all documents where the specified value exists in the array field.
Query for any of multiple values with array-contains-any
Query for any of multiple values with array-contains-any
Use array-contains-any when you want to find documents where the array field contains any one of multiple values. This is useful for filtering by multiple tags or categories at once. You can provide up to 30 comparison values in a single array-contains-any clause.
1import { getFirestore, collection, query, where, getDocs } from 'firebase/firestore';23const db = getFirestore();45// Find articles tagged with 'firebase' OR 'react' OR 'typescript'6const q = query(7 collection(db, 'articles'),8 where('tags', 'array-contains-any', ['firebase', 'react', 'typescript'])9);1011const snapshot = await getDocs(q);12snapshot.forEach((doc) => {13 console.log(doc.id, doc.data().tags);14});Expected result: The query returns documents whose tags array contains at least one of the specified values.
Read and replace an entire array field
Read and replace an entire array field
When you need to reorder elements, remove by index, or perform complex transformations, read the document, modify the array in JavaScript, and write it back. This approach is not atomic — use a transaction if multiple clients might update the same array concurrently.
1import { getFirestore, doc, getDoc, updateDoc } from 'firebase/firestore';23const db = getFirestore();45async function reorderTags(articleId: string, newTagOrder: string[]) {6 // For concurrent safety, wrap in a transaction7 const docRef = doc(db, 'articles', articleId);8 const snap = await getDoc(docRef);910 if (!snap.exists()) throw new Error('Article not found');1112 // Replace the entire tags array13 await updateDoc(docRef, {14 tags: newTagOrder15 });16}1718// Usage: reorder tags alphabetically19const snap = await getDoc(doc(db, 'articles', 'article-1'));20const currentTags = snap.data()?.tags || [];21const sorted = [...currentTags].sort();22await reorderTags('article-1', sorted);Expected result: The tags array is replaced entirely with the new sorted order.
Complete working example
1// Complete Firestore array operations2// Create, read, update, and query array fields34import { initializeApp } from 'firebase/app';5import {6 getFirestore,7 doc,8 setDoc,9 getDoc,10 updateDoc,11 collection,12 query,13 where,14 getDocs,15 arrayUnion,16 arrayRemove,17 serverTimestamp18} from 'firebase/firestore';1920const app = initializeApp({21 apiKey: 'YOUR_API_KEY',22 authDomain: 'YOUR_PROJECT.firebaseapp.com',23 projectId: 'YOUR_PROJECT_ID'24});2526const db = getFirestore(app);2728// Create a document with array fields29export async function createArticle(30 id: string,31 title: string,32 tags: string[]33): Promise<void> {34 await setDoc(doc(db, 'articles', id), {35 title,36 tags,37 collaboratorIds: [],38 createdAt: serverTimestamp()39 });40}4142// Add elements atomically (no duplicates)43export async function addTags(id: string, ...newTags: string[]): Promise<void> {44 await updateDoc(doc(db, 'articles', id), {45 tags: arrayUnion(...newTags)46 });47}4849// Remove elements atomically50export async function removeTags(id: string, ...tagsToRemove: string[]): Promise<void> {51 await updateDoc(doc(db, 'articles', id), {52 tags: arrayRemove(...tagsToRemove)53 });54}5556// Query by single tag57export async function findByTag(tag: string) {58 const q = query(59 collection(db, 'articles'),60 where('tags', 'array-contains', tag)61 );62 const snap = await getDocs(q);63 return snap.docs.map((d) => ({ id: d.id, ...d.data() }));64}6566// Query by any of multiple tags67export async function findByAnyTag(tags: string[]) {68 const q = query(69 collection(db, 'articles'),70 where('tags', 'array-contains-any', tags)71 );72 const snap = await getDocs(q);73 return snap.docs.map((d) => ({ id: d.id, ...d.data() }));74}7576// Read the array and get its length77export async function getTagCount(id: string): Promise<number> {78 const snap = await getDoc(doc(db, 'articles', id));79 return snap.exists() ? (snap.data().tags?.length ?? 0) : 0;80}Common mistakes when storing Arrays in Firestore
Why it's a problem: Trying to update an array element at a specific index, which Firestore does not support
How to avoid: Read the document, modify the array in JavaScript, and write the entire array back. Use a transaction for concurrent safety.
Why it's a problem: Using two array-contains filters in the same query, which Firestore does not allow
How to avoid: Use array-contains-any for matching any of multiple values, or restructure your data to use a map with boolean values instead of an array.
Why it's a problem: Storing complex nested objects in arrays, making them unqueryable and hard to update atomically
How to avoid: Keep array elements as primitive values (strings, numbers). If you need to store objects with multiple fields, use a subcollection or a map field instead.
Best practices
- Use arrayUnion and arrayRemove for atomic add/remove operations instead of reading and rewriting the entire array
- Keep array elements as primitive values (strings, numbers) for reliable querying with array-contains
- Limit arrays to reasonable sizes — arrays with hundreds or thousands of elements increase document size and read costs
- Use array-contains for single-value queries and array-contains-any for multi-value OR queries
- Consider subcollections instead of arrays when the list can grow unbounded or when each item needs its own fields
- Remember that only one array-contains or array-contains-any clause is allowed per compound query
- Use maps with boolean values as an alternative when you need to query for multiple array-like conditions simultaneously
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Show me how to work with arrays in Firestore using the Firebase modular SDK v9+. Include creating documents with arrays, using arrayUnion and arrayRemove for atomic updates, querying with array-contains and array-contains-any, and when to use arrays vs subcollections.
Create a complete TypeScript module for Firestore array operations using the Firebase v9+ modular SDK. Include functions for creating documents with arrays, atomic add/remove with arrayUnion/arrayRemove, array-contains queries, and a comparison of when arrays are appropriate vs subcollections.
Frequently asked questions
Can I store objects (maps) inside a Firestore array?
Yes, Firestore arrays can contain maps (objects). However, array-contains queries compare the entire map by value, so every field must match exactly. This makes querying arrays of objects impractical for most use cases.
Is there a maximum size limit for arrays in Firestore?
There is no explicit array length limit, but the entire document must be under 1 MiB. Each array element counts toward the document size and the 20,000 field limit. Practically, keep arrays under a few hundred elements.
Can I sort elements within a Firestore array?
Firestore does not provide server-side array sorting. Arrays maintain the order in which elements were added. To sort, read the array, sort it in your application code, and write it back.
Why does arrayUnion not add my object to the array?
arrayUnion considers an object a duplicate if all its key-value pairs exactly match an existing element. If even one field differs (including order), it is treated as a new element. Make sure you are not accidentally passing an identical object.
When should I use a subcollection instead of an array?
Use a subcollection when the list can grow indefinitely, when each item has its own metadata or permissions, or when you need to query or paginate the items independently. Use arrays for small, bounded lists of simple values.
Can I use array-contains with orderBy?
Yes. You can combine array-contains with orderBy on a different field. Firestore may require a composite index, which it will prompt you to create via a link in the error message.
Can RapidDev help with Firestore data modeling and array optimization?
Yes. RapidDev can analyze your data access patterns and design efficient Firestore schemas using the right combination of arrays, maps, subcollections, and denormalized fields for your application.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation