# How to Store Arrays in Firestore

- Tool: Firebase
- Difficulty: Beginner
- Time required: 10-15 min
- Compatibility: Cloud Firestore (Spark and Blaze plans), firebase v9+ modular SDK
- Last updated: March 2026

## TL;DR

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.

## Before you start

- 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

### 1. 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.

```
import { getFirestore, doc, setDoc, serverTimestamp } from 'firebase/firestore';

const db = getFirestore();

// Create a document with array fields
await setDoc(doc(db, 'articles', 'article-1'), {
  title: 'Getting Started with Firebase',
  tags: ['firebase', 'tutorial', 'beginner'],
  categories: ['web-development', 'backend'],
  collaboratorIds: ['uid-alice', 'uid-bob'],
  createdAt: serverTimestamp()
});
```

**Expected result:** A document is created with three array fields: tags, categories, and collaboratorIds.

### 2. 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.

```
import { getFirestore, doc, updateDoc, arrayUnion } from 'firebase/firestore';

const db = getFirestore();

// Add new tags to an article (duplicates are ignored)
await updateDoc(doc(db, 'articles', 'article-1'), {
  tags: arrayUnion('advanced', 'cloud-functions')
});

// Add a new collaborator
await updateDoc(doc(db, 'articles', 'article-1'), {
  collaboratorIds: arrayUnion('uid-charlie')
});
```

**Expected result:** The tags array now contains ['firebase', 'tutorial', 'beginner', 'advanced', 'cloud-functions']. The collaboratorIds array includes the new UID.

### 3. 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.

```
import { getFirestore, doc, updateDoc, arrayRemove } from 'firebase/firestore';

const db = getFirestore();

// Remove a tag
await updateDoc(doc(db, 'articles', 'article-1'), {
  tags: arrayRemove('beginner')
});

// Remove a collaborator
await updateDoc(doc(db, 'articles', 'article-1'), {
  collaboratorIds: arrayRemove('uid-bob')
});
```

**Expected result:** The specified elements are removed from the arrays. If an element was not present, no error is thrown.

### 4. 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.

```
import { getFirestore, collection, query, where, getDocs } from 'firebase/firestore';

const db = getFirestore();

// Find all articles tagged with 'firebase'
const q = query(
  collection(db, 'articles'),
  where('tags', 'array-contains', 'firebase')
);

const snapshot = await getDocs(q);
snapshot.forEach((doc) => {
  console.log(doc.id, doc.data().title);
});

// Find all articles where a specific user is a collaborator
const collabQuery = query(
  collection(db, 'articles'),
  where('collaboratorIds', 'array-contains', 'uid-alice')
);

const collabDocs = await getDocs(collabQuery);
collabDocs.forEach((doc) => {
  console.log('Collaboration:', doc.id);
});
```

**Expected result:** The query returns all documents where the specified value exists in the array field.

### 5. 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.

```
import { getFirestore, collection, query, where, getDocs } from 'firebase/firestore';

const db = getFirestore();

// Find articles tagged with 'firebase' OR 'react' OR 'typescript'
const q = query(
  collection(db, 'articles'),
  where('tags', 'array-contains-any', ['firebase', 'react', 'typescript'])
);

const snapshot = await getDocs(q);
snapshot.forEach((doc) => {
  console.log(doc.id, doc.data().tags);
});
```

**Expected result:** The query returns documents whose tags array contains at least one of the specified values.

### 6. 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.

```
import { getFirestore, doc, getDoc, updateDoc } from 'firebase/firestore';

const db = getFirestore();

async function reorderTags(articleId: string, newTagOrder: string[]) {
  // For concurrent safety, wrap in a transaction
  const docRef = doc(db, 'articles', articleId);
  const snap = await getDoc(docRef);

  if (!snap.exists()) throw new Error('Article not found');

  // Replace the entire tags array
  await updateDoc(docRef, {
    tags: newTagOrder
  });
}

// Usage: reorder tags alphabetically
const snap = await getDoc(doc(db, 'articles', 'article-1'));
const currentTags = snap.data()?.tags || [];
const sorted = [...currentTags].sort();
await reorderTags('article-1', sorted);
```

**Expected result:** The tags array is replaced entirely with the new sorted order.

## Complete code example

File: `firestore-arrays.ts`

```typescript
// Complete Firestore array operations
// Create, read, update, and query array fields

import { initializeApp } from 'firebase/app';
import {
  getFirestore,
  doc,
  setDoc,
  getDoc,
  updateDoc,
  collection,
  query,
  where,
  getDocs,
  arrayUnion,
  arrayRemove,
  serverTimestamp
} from 'firebase/firestore';

const app = initializeApp({
  apiKey: 'YOUR_API_KEY',
  authDomain: 'YOUR_PROJECT.firebaseapp.com',
  projectId: 'YOUR_PROJECT_ID'
});

const db = getFirestore(app);

// Create a document with array fields
export async function createArticle(
  id: string,
  title: string,
  tags: string[]
): Promise<void> {
  await setDoc(doc(db, 'articles', id), {
    title,
    tags,
    collaboratorIds: [],
    createdAt: serverTimestamp()
  });
}

// Add elements atomically (no duplicates)
export async function addTags(id: string, ...newTags: string[]): Promise<void> {
  await updateDoc(doc(db, 'articles', id), {
    tags: arrayUnion(...newTags)
  });
}

// Remove elements atomically
export async function removeTags(id: string, ...tagsToRemove: string[]): Promise<void> {
  await updateDoc(doc(db, 'articles', id), {
    tags: arrayRemove(...tagsToRemove)
  });
}

// Query by single tag
export async function findByTag(tag: string) {
  const q = query(
    collection(db, 'articles'),
    where('tags', 'array-contains', tag)
  );
  const snap = await getDocs(q);
  return snap.docs.map((d) => ({ id: d.id, ...d.data() }));
}

// Query by any of multiple tags
export async function findByAnyTag(tags: string[]) {
  const q = query(
    collection(db, 'articles'),
    where('tags', 'array-contains-any', tags)
  );
  const snap = await getDocs(q);
  return snap.docs.map((d) => ({ id: d.id, ...d.data() }));
}

// Read the array and get its length
export async function getTagCount(id: string): Promise<number> {
  const snap = await getDoc(doc(db, 'articles', id));
  return snap.exists() ? (snap.data().tags?.length ?? 0) : 0;
}
```

## Common mistakes

- **Trying to update an array element at a specific index, which Firestore does not support** — undefined Fix: Read the document, modify the array in JavaScript, and write the entire array back. Use a transaction for concurrent safety.
- **Using two array-contains filters in the same query, which Firestore does not allow** — undefined Fix: 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.
- **Storing complex nested objects in arrays, making them unqueryable and hard to update atomically** — undefined Fix: 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

## 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.

---

Source: https://www.rapidevelopers.com/firebase-tutorial/how-to-store-arrays-in-firestore
© RapidDev — https://www.rapidevelopers.com/firebase-tutorial/how-to-store-arrays-in-firestore
