Skip to main content
ansicode
CSI (cursor & erase)

CSI escape codes — cursor, erase, scroll, position

Control Sequence Introducer. The ESC [ family minus its SGR sub-set — cursor movement (up / down / forward / back / column / absolute position), erase (line / display), scroll, save / restore, and the handful of CSI sequences for window and cursor reporting.

60 sequences

When to use CSI

Reach for CSI when you need to move the cursor or change the screen contents — anything positional or structural, not stylistic. CSI sequences open with \x1b[ and finish on a non-m letter: H move to position, J erase display, K erase line, n query state, S / T scroll, r set scroll margins, A / B / C / D step the cursor. Each operation fires the moment it's parsed — there's no persistent CSI mode (that's DEC's lane). Reach for SGR (final byte m) for colour, OSC for window chrome, DEC modes for switches like alt-screen — CSI is the workhorse for everything else.

Common CSI one-liners

Paste any of these into bash / zsh — printf interprets the \x1b byte.

  • Clear screen + cursor home
    printf '\x1b[2J\x1b[H'
  • Erase to end of line
    printf '\x1b[K'
  • Query cursor position (CPR)
    printf '\x1b[6n'

CSI cookbook — six purposes the family covers

CSI (Control Sequence Introducer) is the largest active corner of the escape-code dialect — over 60 finals doing everything except palette and chrome (those are SGR and OSC). Six stops, top to bottom: the envelope syntax itself, cursor movement, screen / line erase, the round-trip queries terminals answer on stdin, scroll-region and margin setup that TUIs use to carve out fixed regions, and local insert / delete / cursor-shape ops that avoid the cost of a full repaint.

  1. 1. The envelope — \x1b[<final-byte>

    Every CSI sequence is \x1b[ (ESC [), an optional private-marker (? for DEC private modes, < / = / > for xterm-defined variants), zero or more numeric parameters separated by ;, and a final byte in the \x40\x7E range that picks the action. Unlike OSC there's no terminator — the final byte ends the sequence. SGR uses m (\x1b[31m), ED uses J, EL uses K, CUP (cursor position) uses H, DSR uses n, DA uses c — over 60 finals in active use across ECMA-48 + xterm. Parameters default to 1 or 0 depending on the action; omitted is equivalent to the default, so \x1b[A (move up 1) is identical to \x1b[1A. There's also an 8-bit CSI form \x9b … you'll see in old VT manuals but never in the wild.

  2. 2. Cursor movement — CUU / CUD / CUF / CUB + CUP + CHA / VPA

    The eight cursor primitives every TUI lives on. CUU / CUD / CUF / CUB (\x1b[NA / B / C / D) move the cursor up / down / forward / backward by N cells (default 1) without crossing the screen edge. CUP (\x1b[r;cH, alias f) jumps to absolute row r, column c (1-indexed). CHA (\x1b[NG) and VPA (\x1b[Nd) jump to absolute column / row keeping the other axis. CNL / CPL (\x1b[NE / F) move N lines down / up but also reset column to 1 — useful for paragraph-style cursor flow. SCOSC / SCORC (\x1b[s / \x1b[u) save / restore one cursor slot — older than DECSC \x1b7 / DECRC \x1b8 but identical in effect on every modern emulator.

  3. 3. Erase — ED J and EL K

    Two finals do all the erasing: J for screen, K for line. Each takes a parameter selecting the range: 0 (default) from cursor to end, 1 from start to cursor, 2 the whole screen / line, and 3 for J also wipes scrollback (xterm extension, supported by most modern emulators). The canonical "clear screen and home" combo every TUI emits on launch is \x1b[2J\x1b[H — erase whole screen, then move cursor to row 1 column 1. ECH \x1b[NX (covered in section 6) is the in-place alternative that overwrites N cells with blanks without moving anything else. For partial repaints, \x1b[K (erase to end of line) after a CUP jump is the cheapest way to wipe a row before redrawing it.

  4. 4. Queries — DA, DA2, DSR, DECRQM

    Round-trip queries where the terminal replies back on stdin — programs that emit a query must read the response or it leaks into the next user input. DA \x1b[c (Primary Device Attributes) asks "what are you?" — modern emulators reply \x1b[?<id>;<feature>;<feature>c advertising VT100 / VT200 / VT400 base + supported features (sixel, regis, colour, etc.). DA2 \x1b[>c returns model + firmware version — useful for distinguishing iTerm2 from kitty from xterm. DSR \x1b[6n (cursor position) returns \x1b[r;cR; \x1b[5n returns terminal-OK status. DECRQM \x1b[?<n>$p reads whether DEC private mode n is currently set / reset / permanent / unsupported (one of 0 / 1 / 2 / 3 / 4 in the reply). Use case: feature-detect alt-screen support before emitting \x1b[?1049h.

  5. 5. Margins & scroll region — DECSTBM r and DECSLRM s

    DECSTBM \x1b[<top>;<bottom>r sets the scrolling region to rows topbottom (1-indexed, inclusive). Inside this region scroll-up / scroll-down (\x1b[NS / \x1b[NT) and IL / DL only affect those rows; the rest of the screen stays put. The classic use is a stationary status bar at the bottom — set \x1b[1;<screenrows-1>r and the status row never scrolls away. To reset to full-screen scrolling, emit \x1b[r with no arguments. DECSLRM \x1b[<left>;<right>s sets horizontal margins the same way, but only takes effect when DECLRMM (\x1b[?69h) is enabled first — most TUIs skip horizontal margins entirely. Without DECLRMM, \x1b[s is interpreted as SCOSC (save cursor) — a classic mode-confusion trap when porting code between terminals.

  6. 6. Insert / delete / cursor shape — IL / DL / ICH / DCH / ECH + DECSCUSR

    Local edits that don't need a full repaint. IL \x1b[NL inserts N blank lines at the cursor, pushing existing content down (off the scroll region). DL \x1b[NM deletes N lines, pulling content up. ICH \x1b[N@ and DCH \x1b[NP do the same on the column axis within a single line. ECH \x1b[NX erases N cells in place — overwrites with blanks without shifting anything else, the right primitive for clearing a known-width field. DECSCUSR \x1b[N q picks cursor shape: 1 blinking block (default), 2 steady block, 3 blinking underline, 4 steady underline, 5 blinking bar, 6 steady bar — vim's terminal-mode toggles between 2 and 6 to show insert vs normal. Honoured by xterm, kitty, iTerm2, wezterm, alacritty, ghostty, Windows Terminal, gnome-terminal 3.16+, konsole; Linux console ignores.

All sequences in this family

Browse other families