JSON Arrays

Updated: August 8, 2026

Ordered lists in JSON — syntax, the guarantees they give you that objects do not, and the API design choices that make them easy or awkward to consume.

Structure

An array is a comma-separated sequence of values in square brackets. Any JSON value may be an element, including other arrays and objects.

["red", "green", "blue"]
[1, 2, 3]
[]
[{ "id": 1 }, { "id": 2 }]
[[1, 2], [3, 4]]

The rules are even shorter than for objects. Elements are separated by commas; the last must not be followed by one; and there are no sparse arrays — you cannot skip a position:

[1, , 3]        // syntax error, though JavaScript allows it
[1, null, 3]    // valid — an explicit gap

Order Is Guaranteed

This is the property that distinguishes arrays from objects and it is the reason to choose one. Objects are unordered by specification; arrays are ordered, and every conforming parser preserves that order exactly.

So whenever sequence carries meaning — search results by relevance, steps in a workflow, a time series, a leaderboard — an array is the correct structure and an object is not. Position is data.

// Order matters → array
"steps": ["download", "verify", "install"]

// Order irrelevant, keys are identifiers → object
"limits": { "maxUsers": 100, "maxStorage": 5000 }

Arrays of Objects

By far the most common shape in real APIs: a list of records, each an object with the same keys.

{
  "data": [
    { "id": 101, "name": "Alice", "role": "admin" },
    { "id": 102, "name": "Bob",   "role": "editor" }
  ],
  "meta": { "page": 1, "perPage": 50, "total": 1284 }
}

The discipline that makes this pleasant to consume is shape consistency. Every element should carry the same keys, even when a value is empty. An optional field that appears on some records and not others forces defensive checks everywhere:

// Awkward — "email" is sometimes absent
[
  { "id": 1, "email": "a@example.com" },
  { "id": 2 }
]

// Better — the key is always present, the value may be null
[
  { "id": 1, "email": "a@example.com" },
  { "id": 2, "email": null }
]

The second form lets a consumer write row.email unconditionally, and lets a schema describe the record precisely.

Mixed Types: Allowed, Rarely Wise

[1, "two", true, null, { "k": "v" }]     // valid JSON

The grammar permits this and it is occasionally the right answer — a heterogeneous event stream, or a tuple-like structure where each position has a fixed distinct meaning. Far more often it signals that the data wanted a different shape, because every consumer now has to inspect each element's type before doing anything with it.

It also complicates typing. In TypeScript a mixed array becomes a union type that must be narrowed at every use; in Go it forces []interface and a type assertion per element. If the elements genuinely differ in kind, an array of objects with an explicit discriminator field is easier to work with:

[
  { "type": "text",  "content": "hello" },
  { "type": "image", "url": "https://example.com/a.png" }
]

Empty Arrays Versus null

A small decision with an outsized effect on how much defensive code your clients write.

{ "roles": [] }      // "no roles" — still an array
{ "roles": null }    // "no roles" — but now a different type

Prefer the empty array. It keeps the field's type stable, so a client can call roles.map(...) or for role in roles without checking anything first. Returning null means every single consumer must remember a null check, and the one that forgets crashes in production.

This is a live trap in Go, where a nil slice marshals to null rather than [] — so a handler that returns an uninitialised slice silently changes its response type. Initialise slices you intend to return as empty. In PHP the equivalent hazard is array_filter leaving gaps in the keys, which turns your array into an object.

Top-Level Arrays

[ { "id": 1 }, { "id": 2 } ]        // valid document

{ "data": [ { "id": 1 } ] }         // usually the better API design

A bare array is perfectly valid JSON. Most well-designed APIs still wrap it in an object, for a practical reason: an object leaves room to grow. Once you have shipped a bare array, adding pagination metadata, a request ID or a warnings field requires changing the response type — a breaking change for every client. With {"data": [...]} you simply add a sibling key.

There is also a historical security note. Top-level arrays were once exploitable via JSON hijacking, because a bare array is a valid JavaScript expression that could be captured by overriding the Array constructor and loading the endpoint through a <script> tag. Modern browsers closed this, but it is why some older style guides forbid top-level arrays outright.

Working With Nested Arrays

{
  "orders": [
    { "id": 9001, "items": [ { "sku": "A1", "qty": 2 } ] }
  ]
}
// JavaScript — flatMap across two levels
const allItems = data.orders.flatMap(order => order.items);

// Safe access into a nested position
const firstSku = data?.orders?.[0]?.items?.[0]?.sku ?? null;

// Python
all_items = [item for order in data["orders"] for item in order["items"]]

Note ?.[0] rather than [0]: indexing an empty array yields undefined rather than throwing, but indexing undefined throws. An empty list is a routine response, so this guard is not paranoia.

For large arrays, a tree view is far more practical than formatted text — each element collapses to one indexed line, so you can see how many there are and inspect any one directly.

Frequently Asked Questions

What is a JSON array?

An ordered list of values in square brackets, separated by commas. Values may be of any JSON type, and unlike objects, an array's ordering is guaranteed by the specification and preserved by every parser.

Can a JSON array hold mixed types?

Yes, the grammar permits it — [1, "two", true, null] is valid. Whether you should is another matter. Mixed arrays force every consumer to type-check each element, and usually indicate that the data wants a different shape.

Can a JSON document be an array at the top level?

Yes. RFC 8259 allows any value as the document root, so a bare array is valid. Many APIs still wrap it in an object anyway, because that leaves room to add pagination or error fields later without a breaking change.

Are sparse arrays allowed in JSON?

No. You cannot skip an index, and [1, , 3] is a syntax error even though JavaScript accepts it. If you need to represent a gap, use null explicitly.

Should an empty result be [] or null?

Almost always []. An empty array keeps the type stable, so clients can iterate it unconditionally. Returning null forces every consumer to add a null check, and forgetting one is a routine source of crashes.

Related Resources

Related Resources