Introduction
A command that dumps raw JSON is fine for a machine and painful for a human. This post uses aux4/render to turn a JSON array into a readable terminal view — a formatted table, then a list with status icons and currency badges — and finally wires it into a custom command's render field so it shows a polished UI in a terminal but still emits plain JSON when piped into another tool.
Install the package
aux4 aux4 pkger install aux4/render
Some data to display
aux4 render reads a JSON array on stdin. We'll use a small task board — save it as tasks.json:
[
{ "title": "Design landing page", "owner": "Sally", "status": "DONE", "priority": "high", "budget": 4200.5 },
{ "title": "Ship billing API", "owner": "Marco", "status": "IN_PROGRESS", "priority": "high", "budget": 15750 },
{ "title": "Write onboarding docs", "owner": "Priya", "status": "TODO", "priority": "medium", "budget": 900 },
{ "title": "Investigate outage", "owner": "Chen", "status": "BLOCKED", "priority": "urgent", "budget": 0 },
{ "title": "Refactor auth module", "owner": "Aisha", "status": "IN_PROGRESS", "priority": "low", "budget": 3300 }
]
Step 1: a formatted table
Pass the columns you want and render table lays them out — aligned, with colored headers in a real terminal:
cat tasks.json | aux4 render table title,owner,status,priority,budget
title owner status priority budget
Design landing page Sally DONE high 4200.5
Ship billing API Marco IN_PROGRESS high 15750
Write onboarding docs Priya TODO medium 900
Investigate outage Chen BLOCKED urgent 0
Refactor auth module Aisha IN_PROGRESS low 3300
A raw 4200.5 isn't money, though. Attach a format to a column with {format:...}:
cat tasks.json | aux4 render table 'title,owner,status,budget{format:currency,currency:USD,locale:en-US}'
title owner status budget
Design landing page Sally DONE $4,200.50
Ship billing API Marco IN_PROGRESS $15,750.00
Write onboarding docs Priya TODO $900.00
Investigate outage Chen BLOCKED $0.00
Refactor auth module Aisha IN_PROGRESS $3,300.00
Step 2: a list with icons and badges
render list gives a card-style view. --primary is the headline, --secondary the subtitle, --badge the right-aligned tag. The real power is two mini-syntaxes:
- Value maps —
field[VALUE:label,...]— swap a raw value for something friendlier (an emoji, a proper label). - Value formats —
field{format:currency,...}— same formatting as the table.
Put them together: a status icon, a human-readable status subtitle, and the budget as a currency badge:
cat tasks.json | aux4 render list \
--primary title \
--icon 'status[TODO:📋,IN_PROGRESS:🔧,DONE:✅,BLOCKED:🚫]' \
--secondary 'status[TODO:To Do,IN_PROGRESS:In Progress,DONE:Done,BLOCKED:Blocked]' \
--badge 'budget{format:currency,currency:USD,locale:en-US}'
✅ Design landing page $4,200.50
Done
🔧 Ship billing API $15,750.00
In Progress
📋 Write onboarding docs $900.00
To Do
🚫 Investigate outage $0.00
Blocked
🔧 Refactor auth module $3,300.00
In Progress
The same data, now scannable. --primary also interpolates fields with $ — --primary '$owner: $title' — and unmapped values fall through unchanged.
Step 3: other views
A single record as key/value (--index selects the row):
cat tasks.json | aux4 render kv --index 0
title=Design landing page
owner=Sally
status=DONE
priority=high
budget=4200.5
Or as YAML — selected fields across all records:
cat tasks.json | aux4 render yaml 'title,status'
- title: Design landing page
status: DONE
- title: Ship billing API
status: IN_PROGRESS
- title: Write onboarding docs
status: TODO
- title: Investigate outage
status: BLOCKED
- title: Refactor auth module
status: IN_PROGRESS
render csv rounds out the set for spreadsheet exports.
Step 4: wire it into a command
Here's the payoff. Instead of typing that long render list every time, attach it to a command in an .aux4 file. The render field maps output modes to render commands — tty runs automatically when output is a terminal. Create .aux4:
{
"profiles": [
{
"name": "main",
"commands": [
{
"name": "tasks",
"execute": ["json:cat tasks.json"],
"render": {
"tty": "aux4 render list --primary title --icon 'status[TODO:📋,IN_PROGRESS:🔧,DONE:✅,BLOCKED:🚫]' --secondary 'status[TODO:To Do,IN_PROGRESS:In Progress,DONE:Done,BLOCKED:Blocked]' --badge 'budget{format:currency,currency:USD,locale:en-US}'",
"table": "aux4 render table title,owner,status,priority,budget"
},
"help": { "text": "List the project tasks." }
}
]
}
]
}
The json: executor captures the JSON into the command's response. Now run it in a terminal and render.tty fires automatically:
aux4 tasks
✅ Design landing page $4,200.50
Done
🔧 Ship billing API $15,750.00
In Progress
📋 Write onboarding docs $900.00
To Do
🚫 Investigate outage $0.00
Blocked
🔧 Refactor auth module $3,300.00
In Progress
But pipe it — no terminal, no rendering — and you get the raw JSON back, untouched, ready for jq or another command:
aux4 tasks | jq '.[0]'
{
"budget": 4200.5,
"owner": "Sally",
"priority": "high",
"status": "DONE",
"title": "Design landing page"
}
That's the whole trick: one command, a human-friendly UI for a person and clean data for a pipeline. Any other named mode is on demand — render.table here is aux4 tasks --render table:
aux4 tasks --render table
title owner status priority budget
Design landing page Sally DONE high 4200.5
Ship billing API Marco IN_PROGRESS high 15750
Write onboarding docs Priya TODO medium 900
Investigate outage Chen BLOCKED urgent 0
Refactor auth module Aisha IN_PROGRESS low 3300
Conclusion
aux4/render turns a JSON array into a table, a key/value block, YAML, CSV, or a MUI-style list with icons and currency badges — and its render field lets any command detect a terminal and show a polished UI there while staying pipe-friendly everywhere else. Give your commands a face without giving up their data.