Pretty-print JSON
RunThe identity filter . returns the input unchanged, formatted and indented.
jq '.'
- Input
{"name":"Alice","tags":["admin","dev"],"active":true}- Output
{ "name": "Alice", "tags": [ "admin", "dev" ], "active": true }
Test jq filters on your JSON instantly. This online jq playground runs the real jq 1.8.2 compiled to WebAssembly, right in your browser: no sign-up, and your data never leaves your device.
Output updates as you type. New to jq? Start from the jq examples below or read the jq manual.
jq Playground for VS Code turns jq into a notebook: keep filters in .jqpg files, run them against workspace files, URLs and shell commands, with autocomplete and Copilot-powered explain and fix. 33k+ installs, free and open source.
Common jq filters with sample input and the exact output of jq 1.8.2. Press Run to load any example into the playground and edit it.
Access fields, arrays and nested values.
The identity filter . returns the input unchanged, formatted and indented.
jq '.'
{"name":"Alice","tags":["admin","dev"],"active":true}{
"name": "Alice",
"tags": [
"admin",
"dev"
],
"active": true
}Use .key to read a single field. Missing keys return null instead of failing.
jq '.name'
{"name": "Alice", "email": "alice@example.com"}"Alice"
Chain keys to walk into nested objects.
jq '.user.address.city'
{"user": {"address": {"city": "Lugano", "zip": "6900"}}}"Lugano"
.[0] is the first element, .[-1] the last, .[1:3] a slice.
jq '[.[0], .[-1], .[1:3]]'
["a", "b", "c", "d"]
["a","d",["b","c"]]
.[] emits every element; pipe each one into the next filter.
jq '.users[].name'
{"users": [{"name": "Alice"}, {"name": "Bob"}]}"Alice" "Bob"
keys returns sorted object keys; length counts elements, keys or characters.
jq '{keys: keys, count: length}'{"b": 2, "a": 1, "c": 3}{"keys":["a","b","c"],"count":3}Keep only the values you need.
select(cond) keeps the values for which the condition is true.
jq 'map(select(.active) | .name)'
[{"name": "Alice", "age": 34, "active": true, "dept": "eng"}, {"name": "Bob", "age": 27, "active": false, "dept": "ops"}, {"name": "Carol", "age": 41, "active": true, "dept": "eng"}]["Alice","Carol"]
Combine select with comparison operators.
jq '.[] | select(.age > 30) | .name'
[{"name": "Alice", "age": 34, "active": true, "dept": "eng"}, {"name": "Bob", "age": 27, "active": false, "dept": "ops"}, {"name": "Carol", "age": 41, "active": true, "dept": "eng"}]"Alice" "Carol"
test(regex) returns true when the string matches.
jq 'map(select(.email | test("@example\\.com$")))'[{"email": "alice@example.com"}, {"email": "bob@test.org"}][{"email":"alice@example.com"}]index(value) or any(...) find a value inside an array.
jq 'map(select(any(.tags[]; . == "jq")) | .name)'
[{"name": "a", "tags": ["jq", "json"]}, {"name": "b", "tags": ["yaml"]}]["a"]
has(key) tests for a key, even when its value is null.
jq 'map(has("email"))'[{"email": null}, {"name": "Bob"}][true,false]
first(expr) stops after the first output.
jq 'first(.[] | select(.age > 30)) | .name'
[{"name": "Alice", "age": 34, "active": true, "dept": "eng"}, {"name": "Bob", "age": 27, "active": false, "dept": "ops"}, {"name": "Carol", "age": 41, "active": true, "dept": "eng"}]"Alice"
Reshape objects and arrays.
map(f) applies a filter to each array element.
jq 'map(.price * .qty)'
[{"price": 3, "qty": 2}, {"price": 10, "qty": 1}][6,10]
Use {} to pick and rename fields.
jq 'map({user: .name, isAdult: (.age >= 18)})'[{"name": "Alice", "age": 34}, {"name": "Tim", "age": 12}][{"user":"Alice","isAdult":true},{"user":"Tim","isAdult":false}]del(path) removes fields, e.g. secrets before sharing a payload.
jq 'del(.password, .token)'
{"user": "alice", "password": "s3cret", "token": "abc"}{"user":"alice"}|= updates the value at a path and keeps the rest of the document.
jq '.items[].price |= . * 2'
{"items": [{"id": 1, "price": 5}, {"id": 2, "price": 8}]}{"items":[{"id":1,"price":10},{"id":2,"price":16}]}with_entries lets you rewrite keys and values of an object.
jq 'with_entries(.key |= ascii_upcase)'
{"host": "localhost", "port": 8080}{"HOST":"localhost","PORT":8080}The alternative operator // replaces null or false with a fallback.
jq 'map(.nickname // "anonymous")'
[{"nickname": "ally"}, {"name": "Bob"}]["ally","anonymous"]
jq has full if ... then ... elif ... else ... end expressions.
jq 'map(if .score >= 60 then "pass" else "fail" end)'
[{"score": 72}, {"score": 45}]["pass","fail"]
* deep-merges two objects; add merges an array of objects.
jq '.defaults * .overrides'
{"defaults": {"debug": false, "db": {"host": "localhost", "port": 5432}}, "overrides": {"db": {"host": "db.internal"}}}{"debug":false,"db":{"host":"db.internal","port":5432}}Summaries and reports from arrays of objects.
sort_by(f) sorts ascending; add reverse for descending order.
jq 'sort_by(.age) | reverse | map(.name)'
[{"name": "Alice", "age": 34, "active": true, "dept": "eng"}, {"name": "Bob", "age": 27, "active": false, "dept": "ops"}, {"name": "Carol", "age": 41, "active": true, "dept": "eng"}]["Carol","Alice","Bob"]
group_by(f) groups equal values; build one summary object per group.
jq 'group_by(.dept) | map({dept: .[0].dept, count: length})'[{"name": "Alice", "age": 34, "active": true, "dept": "eng"}, {"name": "Bob", "age": 27, "active": false, "dept": "ops"}, {"name": "Carol", "age": 41, "active": true, "dept": "eng"}][{"dept":"eng","count":2},{"dept":"ops","count":1}]add sums numbers (and concatenates strings or arrays).
jq 'map(.amount) | add'
[{"amount": 12.5}, {"amount": 7.5}, {"amount": 30}]50
unique sorts and removes duplicates; unique_by(f) dedupes by a key.
jq 'map(.tag) | unique'
[{"tag": "b"}, {"tag": "a"}, {"tag": "b"}]["a","b"]
min_by(f) and max_by(f) return the whole matching object.
jq '{cheapest: min_by(.price).name, priciest: max_by(.price).name}'[{"name": "pen", "price": 2}, {"name": "book", "price": 15}, {"name": "mug", "price": 8}]{"cheapest":"pen","priciest":"book"}reduce folds an array into a single value, here an id-to-name map.
jq 'reduce .[] as $u ({}; .[$u.id] = $u.name)'[{"id": "u1", "name": "Alice"}, {"id": "u2", "name": "Bob"}]{"u1":"Alice","u2":"Bob"}Text, CSV, Base64 and raw output.
Embed expressions in strings with \(...).
jq -r '.[] | "\(.name) is \(.age) years old"'
[{"name": "Alice", "age": 34}, {"name": "Bob", "age": 27}]Alice is 34 years old Bob is 27 years old
split(sep) and join(sep) convert between strings and arrays.
jq '.path | split("/") | map(select(. != "")) | join(" > ")'{"path": "/usr/local/bin"}"usr > local > bin"
Build an array per row and format it with @csv; use raw output (-r).
jq -r '["name", "age"], (.[] | [.name, .age]) | @csv'
[{"name": "Alice", "age": 34}, {"name": "Bob", "age": 27}]"name","age" "Alice",34 "Bob",27
to_entries exposes key/value pairs, handy for .env files.
jq -r 'to_entries[] | "\(.key)=\(.value)"'
{"HOST": "localhost", "PORT": 8080}HOST=localhost PORT=8080
@base64 encodes a string, @base64d decodes it.
jq '{encoded: (.text | @base64), decoded: (.b64 | @base64d)}'{"text": "hello jq", "b64": "anNvbg=="}{"encoded":"aGVsbG8ganE=","decoded":"json"}fromjson parses an escaped JSON string; tojson does the opposite.
jq '.payload | fromjson | .id'
{"payload": "{\"id\": 42, \"ok\": true}"}42
todate formats seconds since epoch as ISO 8601; strftime gives custom formats.
jq '.created | {iso: todate, day: strftime("%Y-%m-%d")}'{"created": 1700000000}{"iso":"2023-11-14T22:13:20Z","day":"2023-11-14"}Recursion, paths and multiple inputs.
.. walks every value recursively; .id? // empty keeps only ids.
jq '[.. | .id? // empty]'
{"id": 1, "children": [{"id": 2, "children": [{"id": 3}]}]}[1,2,3]
paths(f) returns the path to every value matching f; scalars matches leaves (but skips false and null).
jq '[paths(scalars) | map(tostring) | join(".")]'{"a": {"b": 1, "c": ["x", "y"]}}["a.b","a.c.0","a.c.1"]
flatten removes nesting; pass a depth to flatten only some levels.
jq 'flatten'
[1, [2, [3, [4]]]]
[1,2,3,4]
With -s jq reads all inputs into one array, e.g. for NDJSON logs.
jq -s 'map(.ms) | add / length'
{"ms": 120}
{"ms": 80}
{"ms": 100}100
With -n jq ignores input; useful for building JSON from scratch.
jq -n '[range(1; 4) | {id: ., label: "item-\(.)"}]'(none, -n)
[{"id":1,"label":"item-1"},{"id":2,"label":"item-2"},{"id":3,"label":"item-3"}]The flags you can toggle in the playground, and what they do.
| Flag | Long form | What it does |
|---|---|---|
| -r | --raw-output | Print strings without JSON quotes, e.g. for CSV or shell scripts. |
| -c | --compact-output | Print each JSON value on a single line. |
| -s | --slurp | Read every input value into one array before running the filter. |
| -n | --null-input | Ignore the input and run the filter once with null. |
| -S | --sort-keys | Sort the keys of every object in the output. |
Yes. The jq playground is free, needs no account and has no usage limits. It is built by the author of the jq Playground extension for VS Code.
No. jq 1.8.2 is compiled to WebAssembly and runs inside your browser, in a Web Worker. Your filter and JSON never leave your device, so you can safely test filters on private API responses or logs. Share links store the filter and input in the URL fragment, which browsers do not send to the server.
The playground runs jq 1.8.2, the official jq source code compiled to WebAssembly with the jq-wasm package. Filters behave exactly like the jq command-line tool.
jqplay.org is another popular online jq playground. This one runs jq entirely in your browser, lets you share a filter with a link, and comes with a cheat sheet of runnable jq examples. When you outgrow the browser, the same filters run in VS Code with the jq Playground extension.
Not in the browser playground, which works on pasted JSON. The jq Playground extension for VS Code runs filters against workspace files, URLs and shell command output, and saves them in reusable .jqpg notebooks. See the VS Code extension →
To keep the page responsive, the playground stops a filter that runs for more than 3 seconds, which usually means an infinite generator such as repeat or range(infinite). Add limit(n; ...) or first(...) to take only the values you need.