Skip to content
InnovateTechie
Claude Code

Claude Code Statusline: Setup and Customization

InnovateTechieBy InnovateTechie10 min read
Share
Claude Code Statusline: Setup and Customization

Part ofWhat Is Claude Code? The Complete Guide

The Claude Code statusline puts model, context and cost in your terminal. Setup in settings.json, custom scripts and recipes worth copying.

The statusline in Claude Code is a configurable bar pinned to the bottom of the CLI that shows whatever a script you point at it prints: model, context usage, session cost, git branch. Turn it on by adding a statusLine command to ~/.claude/settings.json, or run /statusline and describe the bar you want in plain English.

We run this site with Claude Code daily, and the Claude Code statusline is the first thing we set up on any new machine — we lost too many sessions to surprise compactions before we kept context usage on screen. Below: what the bar can show, both ways to enable it, a script you can copy, the prebuilt options, and the fixes for a blank bar. New to the agent itself? Start with our pillar guide, What Is Claude Code?, then come back.

What the statusline in Claude Code shows

Nothing, at first — the bar stays empty until you configure it. Once you do, Claude Code pipes a JSON snapshot of the live session to your command on every update and renders whatever the command prints to stdout in a row above the built-in footer. Think of it as the VS Code status bar, except every segment is a line of shell you control.

The JSON carries more than most people expect. These are the fields we reach for:

FieldWhat you get
model.display_name, model.idActive model — Opus with id claude-opus-4-8, for example
context_window.used_percentagePrecomputed share of the context window consumed (input plus cache tokens)
context_window.context_window_size200,000 tokens by default; 1,000,000 on extended-context models
cost.total_cost_usd, cost.total_duration_msEstimated session spend and wall-clock duration
cost.total_lines_added, cost.total_lines_removedCode churn for the session
workspace.current_dir, workspace.project_dirWhere you are now versus where you launched
rate_limits.five_hour, rate_limits.seven_daySubscription quota used — Pro and Max sessions only
session_id, version, vim.mode, agent.nameSession identity and UI state for everything else

Two details trip people up. First, cost.total_cost_usd is a client-side estimate — good for pacing yourself, not invoice-exact. Second, the git branch is not in the JSON: your script asks git directly, which is why every example below shells out to git branch --show-current. And if you're unsure why that context percentage deserves permanent screen space, our Claude context window guide shows what degrades as it fills.

Claude Code statusline config: two ways to turn it on

The fast path is the /statusline command. Type it in a session, describe the result in natural language — /statusline show model, git branch, and a context bar that goes red past 80% — and Claude Code writes the script into ~/.claude/, makes it executable, and adds the settings entry itself. We use it for a first draft, then hand-edit.

The manual path is a statusLine block in your settings file. Anthropic documents every stdin field, with bash, Python, and Node.js examples, in the official statusline reference; the minimal working config is four lines:

{
  "statusLine": {
    "type": "command",
    "command": "~/.claude/statusline.sh",
    "padding": 0
  }
}

Five keys cover the whole Claude Code statusline config surface:

KeyPurposeDefault
typeAlways "command" — the bar runs a shell commandrequired
commandScript path or inline shell; receives session JSON on stdinrequired
paddingExtra horizontal spacing, in characters0
refreshIntervalAlso re-run every N seconds (minimum 1) for clock-style segmentsunset
hideVimModeIndicatorHide the built-in -- INSERT -- text when your script prints vim mode itselffalse

You don't even need a script file. The command string runs in a shell, so an inline jq one-liner is a complete configuration:

{
  "statusLine": {
    "type": "command",
    "command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'"
  }
}

Like Claude Code skills, this is all plain files — no marketplace, no build step — and the same block placed in .claude/settings.json at a repo root gives a whole team the same bar. statusLine is one of a few dozen settings worth knowing; our Claude Code CLI documentation guide maps the rest.

Statusline in Claude Code data flow — session JSON piped to your script on stdin, stdout rendered as the status bar

Build a custom Claude Code statusline script

The contract is tiny: read one JSON object from stdin, print text to stdout. Each printed line becomes a row, and ANSI color codes work in any modern terminal. Here's the bash script we actually run, trimmed of decoration:

#!/bin/bash
input=$(cat)
 
MODEL=$(echo "$input" | jq -r '.model.display_name')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
BRANCH=$(git branch --show-current 2>/dev/null)
 
printf '[%s] ctx %s%% | $%.2f%s\n' "$MODEL" "$PCT" "$COST" "${BRANCH:+ | $BRANCH}"

Save it as ~/.claude/statusline.sh, run chmod +x on it, and point the settings block above at the path. Test it before Claude Code does — echo '{"model":{"display_name":"Opus"}}' | ~/.claude/statusline.sh should print a line instantly. The // 0 fallbacks earn their keep: used_percentage is null until the first API response, and an unguarded script prints null% for the opening seconds of every session.

Timing shapes how you design segments. The script runs after each assistant message, after /compact finishes, and when permission mode or vim mode changes, debounced at 300 ms so rapid events batch into one execution. A slow script blocks the next render and gets cancelled when a fresh update lands, so cache anything expensive — git status in a large repository is the usual offender. The whole thing runs locally and consumes no API tokens.

ccusage, ccstatusline, and when not to script it

If what you want is cost telemetry, don't build it — the community already has. Point your command at ccusage, whose statusline integration reads your local usage data and prints session cost, today's total, the current five-hour billing block with time remaining, a burn rate, and the active model:

{
  "statusLine": {
    "type": "command",
    "command": "npx -y ccusage statusline",
    "padding": 0
  }
}

ccstatusline solves a different problem: appearance. It ships powerline glyphs, themes, and an interactive terminal UI where you assemble segments instead of writing shell, and Anthropic's docs also point Starship users at starship-claude. Our rough guide:

OptionSetupBest for
Custom scriptTen lines of bash plus jqExact control, zero dependencies, odd personal requests
ccusagenpx -y ccusage statuslineCost telemetry: daily totals, billing blocks, burn rate
ccstatuslineInteractive TUI configuratorPowerline styling without writing any code

We started with ccusage, kept it for a month, then went back to a custom script — the burn-rate figure was fun, but branch plus context percentage is what we glance at forty times a day.

Claude Code statusline recipes — context percentage bar, session cost, model name, and git branch segments

Recipes: the segments we actually run

Context percentage, colored. Read context_window.used_percentage, print a ten-character block bar, switch the ANSI color yellow at 70% and red at 90%. This is the highest-value segment on the bar: when it goes red, we /compact on our own terms instead of mid-task.

Session cost. printf '$%.2f' on cost.total_cost_usd. API-billed accounts watch real spend; subscription accounts read it as an effort meter for the session.

Model and effort. Print model.display_name beside effort.level. It has caught us running Claude Opus 4.8 at max effort on README tweaks more than once — a two-second glance that pays for itself.

Rate limits. Subscriber sessions expose rate_limits.five_hour.used_percentage and a seven_day counterpart. On Claude Max plans the five-hour window is the one you bump first, and watching it climb beats the surprise lockout by a mile.

Why your Claude Code status bar is empty

A statusline in Claude Code stays blank until something is configured, so an empty bar out of the box is not a bug. If yours is configured and still blank, the causes are boringly consistent:

SymptomCauseFix
No bar at allNo statusLine entry — it is off by defaultRun /statusline or add the settings block
Configured but blankScript not executable, or printing to stderrchmod +x the file; write to stdout only
statusline skipped · restart to fixWorkspace trust not acceptedRestart Claude Code and accept the trust dialog
Worked, then vanisheddisableAllHooks: true in settingsRemove the flag — it disables the statusline too
Prints null or 0Fields empty before the first API responseAdd fallbacks such as // 0 in jq
Fine in a terminal, missing in the editor panelThe extension pane does not render itRun Claude Code in the integrated terminal

Two sharper edges. On Windows, write the command path with forward slashes — Git Bash consumes unquoted backslashes, and the command fails with no visible error. And claude --debug logs the exit code and stderr of the first statusline invocation in a session, which turns most of this table into a thirty-second diagnosis.

The quick checklist:

  • Set the statusLine command in settings.json
  • Your script reads session JSON on stdin
  • Whatever it prints becomes the bar
  • Start with context %, cost and model

Claude pricing at a glance

PlanPrice
Free$0
Pro$20 / month
Maxfrom $100 / month
APIPay per token

For the full breakdown of every plan, see our how much Claude costs guide.

Frequently Asked Questions

You describe the bar you want in plain language — /statusline show model name and context percentage with a progress bar — and Claude Code writes the script, saves it under ~/.claude/, and wires up the settings entry itself. Run it again anytime to tweak the design, or ask it to remove the statusline entirely.

One JSON object per update, on stdin. It carries the model id and display name, current and project directories, session cost and duration, context-window token counts and percentages, rate-limit usage, session id, Claude Code version, output style, vim mode, and open pull-request details. Fields that don't apply are simply absent.

No. The statusline is a terminal feature, and the extension's graphical panel doesn't render it. If you want the bar while working in your editor, run Claude Code in the integrated terminal instead of the panel — our [Claude Code VS Code](/claude-code-vs-code) guide walks through both setups side by side.

Yes. A statusLine entry in .claude/settings.json at the repository root overrides your user-level config, and it travels with the repo — everyone who clones the project gets the same bar. We use this to pin a branch-plus-context display on shared codebases while keeping a cost-heavy personal setup elsewhere.

It runs entirely on your machine and consumes zero API tokens. Speed depends on your script: slow commands such as git status in huge repositories delay updates, and a new trigger cancels the in-flight run. Keep the script fast, or cache expensive calls to a temp file keyed by session id.

Yes. Subscriber sessions receive a ratelimits object with fivehour and sevenday windows, each carrying a used percentage and a reset timestamp. Print them to watch quota drain in real time. The fields appear only after the first API response, so guard against their absence in your script.

After every assistant message, after /compact finishes, and whenever permission mode or vim mode changes — debounced at 300 milliseconds so rapid events batch into one run. For clock-style segments that should tick while the session sits idle, add a refreshInterval value in seconds to the statusLine config.
InnovateTechie

Written by

InnovateTechie

Writing about Claude and the Anthropic toolkit — models, Claude Code, pricing, features, and fixes, in clear, practical, hands-on guides tested by daily use.

View all posts →