
Rendering Layout Performance
- 42 installs
- 50 repo stars
- Updated June 18, 2026
- josiahsiegel/claude-plugin-marketplace
Helps with ai & agent building tasks.
About
rendering-layout-performance is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- rendering-layout-performance
- AI & Agent Building
- AI-coding skill
Rendering Layout Performance by the numbers
- 42 all-time installs (skills.sh)
- +4 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #8,060 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/josiahsiegel/claude-plugin-marketplace --skill rendering-layout-performanceAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 42 |
|---|---|
| repo stars | ★ 50 |
| Last updated | June 18, 2026 |
| Repository | josiahsiegel/claude-plugin-marketplace ↗ |
What it does
Helps with ai & agent building tasks.
Files
TUI Rendering, Layout, Performance, and Alternate Screen Lifecycle
Use this skill when a TUI draws incorrectly, flickers, wastes CPU, mishandles resize, corrupts scrollback, or needs a robust rendering architecture.
Rendering principles
1. Render from state. The view should be a pure projection of current state and terminal dimensions. 2. Batch writes. Build a frame, diff or queue updates, then flush once. Avoid many small writes. 3. Minimize terminal control churn. Repeated style resets, cursor moves, and full clears are expensive and visible over remote links. 4. Throttle high-frequency sources. Coalesce logs, metrics, mouse motion, resize storms, and progress updates. 5. Define frame ownership. A full-screen renderer owns the screen while active; background output goes elsewhere.
Layout rules
- Compute layout from
widthandheightevery frame or on every resize event. - Establish minimum usable dimensions and render a clear small-screen message below that threshold.
- Prefer constraints, flex, grid, splits, or framework layout primitives over magic coordinates.
- Keep focus, selection, viewport offset, and cursor position in UI state.
- Test narrow, wide, tall, tiny, and odd terminal sizes.
Alternate screen lifecycle
Use alternate screen for full-screen apps when users should return to the original scrollback after exit. Do not use it for simple output where command results should remain visible.
Cleanup must restore:
- Alternate screen.
- Raw/cbreak mode.
- Mouse tracking.
- Bracketed paste.
- Focus and extended keyboard modes.
- Cursor visibility and style.
- Terminal title or palette changes if modified.
Performance checklist
- UI loop does not block on network, disk, child processes, or API calls.
- Long operations publish progress through messages/channels/tasks.
- Render rate is capped to useful human perception, especially for dashboards.
- Resize events are coalesced before expensive recalculation.
- Tables and lists virtualize or page large datasets.
- Logging does not write to stdout/stderr while the TUI owns the screen.
- Remote terminals and SSH latency are part of testing if supported.
Failure modes
| Symptom | Likely cause | Fix |
|---|---|---|
| Flicker | clearing entire screen or flushing repeatedly | diff/batch writes, avoid full clears |
| High CPU while idle | unconditional redraw loop | render on events or capped tick |
| Corrupted shell after crash | missing cleanup on panic/exception | central terminal guard and finally/defer/drop cleanup |
| Layout overlap | absolute coordinates or stale dimensions | recompute constraints after resize |
| Logs appear inside UI | stdout/stderr logging while alternate screen active | file sink, in-app log panel, or buffered logs |
Reference files
references/render-loop-patterns.md- Frame buffers, dirty regions, throttling, and event loops.references/alternate-screen-lifecycle.md- Setup/teardown checklist and crash recovery.
Alternate Screen Lifecycle Reference
Setup order
1. Verify stdin/stdout are TTYs or the framework has an explicit terminal handle. 2. Register cleanup before enabling modes. 3. Enable raw/cbreak mode. 4. Enter alternate screen if this is a full-screen TUI. 5. Enable mouse, bracketed paste, focus, or keyboard extensions only when needed. 6. Hide cursor only if the renderer controls cursor restoration. 7. Start the event loop.
Teardown order
1. Stop workers or detach their output from the terminal. 2. Disable optional modes: mouse, paste, focus, extended keyboard. 3. Show/restore cursor. 4. Leave alternate screen. 5. Disable raw/cbreak mode. 6. Flush final output and restore logging.
Crash recovery
Install language-appropriate panic, exception, signal, and cancellation handlers. Cleanup code must tolerate partially initialized state and repeated calls. If the display is corrupted, document recovery commands such as reset, stty sane, reopening the terminal tab, or disabling the TUI with --no-tui.
When not to use alternate screen
Do not use alternate screen for single progress bars, command output users expect to copy, non-interactive reports, or errors that should remain in scrollback. Inline progress regions are often better for package managers, build tools, and CI-friendly logs.
Render Loop Patterns
Event-driven loop
The most efficient TUI renders when state changes. Inputs include key events, mouse events, paste events, resize notifications, timers, worker messages, and data updates. Merge them into an update path that decides whether a redraw is needed.
while running:
msg = next(input, resize, timer, worker)
model, effects, dirty = update(model, msg)
schedule(effects)
if dirty or msg is Resize:
frame = view(model, terminal_size)
diff_and_flush(previous_frame, frame)
previous_frame = frameTick-driven loop
Use ticks for animation, spinners, clocks, polling dashboards, and transient status. Cap the tick rate. Many TUIs feel responsive at 10 to 30 FPS for animation and much lower for dashboards. Prefer no tick when idle.
Rendering architectures
Immediate-mode TUIs redraw a complete logical view from state each frame and rely on diffing/batching to make terminal writes efficient. Retained-mode TUIs keep a widget tree with lifecycle, invalidation, and focus state. Both can work well; immediate mode favors deterministic rendering and simple state flow, while retained mode favors rich widgets and local component state.
Dirty-region and diff rendering
Frameworks often maintain a previous frame and emit only changed cells. If you build this yourself:
- Model each cell as grapheme/text, style, and width metadata.
- Compare previous and next grids.
- Group adjacent changed cells by row and compatible style.
- Move cursor, write runs, reset styles deliberately, and flush once.
- Track double-width characters carefully; clearing one half corrupts layout.
- Invalidate neighboring cells when wide glyphs, combining marks, or style spans change.
Tiny cell-buffer diff pseudo-code:
diff(prev, next):
writes = []
for y in 0..height:
x = 0
while x < width:
if prev[y][x] == next[y][x]:
x += 1; continue
start = x
style = next[y][x].style
text = ""
while x < width and prev[y][x] != next[y][x] and next[y][x].style == style:
text += next[y][x].grapheme_or_space
if next[y][x].width == 2: mark_following_cell_consumed()
x += max(1, next[y][x].width)
writes.append(move(y,start) + sgr(style) + text)
return concat(writes) + sgr(reset) + flushDamage tracking can operate at full-grid, row, rectangular-region, or run level. Simpler full-frame diffing is usually fast enough; finer damage tracking helps dashboards and remote links only when it does not add correctness bugs.
Layout recipes
Sidebar/detail
Use for navigation plus contextual detail. Collapse sidebar first on narrow screens.
80x24
┌──────────────┬──────────────────────────────────────────────────────────────┐
│ Projects │ Project api-service │
│ > api │ Status: passing │
│ web │ │
│ worker │ Recent jobs │
│ │ 10:31 build ok │
│ │ 10:28 test ok │
├──────────────┴──────────────────────────────────────────────────────────────┤
│ q quit / search Enter open ? help │
└─────────────────────────────────────────────────────────────────────────────┘Rules: keep selection state in sidebar, detail scroll state separate, and expose the selected item textually (> or label) without relying only on color.
Table/detail
Use for data explorers where the table is primary and detail changes with selection.
┌─────────────────────────────────────────────────────────────────────────────┐
│ Filter: error_ 12 results │
├────┬──────────────┬──────────┬──────────────────────────────────────────────┤
│ > │ api-17 │ failed │ timeout waiting for db │
│ │ api-18 │ running │ migrations │
├────┴──────────────┴──────────┴──────────────────────────────────────────────┤
│ Detail api-17: retries=3 duration=62s owner=platform │
│ Actions: Enter open r retry c copy id │
└─────────────────────────────────────────────────────────────────────────────┘Rules: right-align numbers, truncate prose predictably, preserve horizontal scroll/column hiding policy, and render only visible rows.
Wizard
Use for linear setup where progress and validation matter.
┌─ Setup database ────────────────────────────────────────────────────────────┐
│ Step 2 of 4: Connection │
│ │
│ Host [db.internal________________] │
│ Port [5432____] │
│ SSL (x) require ( ) disable │
│ │
│ Error: Port must be a number from 1 to 65535 │
│ │
│ Back: Esc/B Next: Enter Cancel: Ctrl+C │
└─────────────────────────────────────────────────────────────────────────────┘Rules: keep validation close to fields, support back/cancel, and provide non-interactive flags/config for automation.
Dashboard
Use for monitoring, but throttle updates and include units.
┌ CPU 42% ──────┐ ┌ Memory 7.2/16 GiB ┐ ┌ Errors 3 last 5m ┐
│ ▁▂▃▅▆▅▃▂ │ │ ███████░░░░░░░░░ │ │ api 2 web 1 │
└───────────────┘ └───────────────────┘ └─────────────────┘
┌ Logs ───────────────────────────────────────────────────────────────────────┐
│ 10:31 api timeout │
│ 10:30 web recovered │
└─────────────────────────────────────────────────────────────────────────────┘Rules: charts are summaries, not the only data. Add textual values, reduce motion over SSH/CI, and coalesce frequent metric updates.
Tiny-terminal fallback
Render a useful fallback instead of broken panes.
32x8
App needs 60x15 for full UI.
Current: 32x8
Use:
app --plain status
app --json status
q quitRules: define minimum sizes per view, never panic on zero/very small dimensions, and keep quit/help visible.
Layout and viewport management
Use constraint, flex, grid, or split layouts so panes respond to terminal dimensions. Define minimum sizes for each region and a tiny-terminal fallback. Scroll regions and viewports need explicit state: content length, visible range, offset, cursor/selection, horizontal scroll, and resize behavior. Terminal hardware scroll regions can be efficient but complicate diffing; prefer framework abstractions unless building a pager or log viewer.
Virtualization
Large lists and tables should render only visible rows plus small overscan. Keep selection and scroll offset in state. Avoid materializing huge styled strings every frame.
Profiling and performance budgets
Measure frame time, bytes written per frame, flush count, input-to-render latency, allocation rate, and idle CPU. In managed runtimes, watch GC pressure from rebuilding large styled strings or widget trees every tick. Optimize only after identifying whether the bottleneck is layout, text measurement, diffing, terminal I/O, or data processing.
Remote terminal performance
SSH, containers, serial links, and multiplexers amplify tiny-write costs. Reduce cursor movement, avoid full-screen clears, compress updates into contiguous writes, and throttle high-frequency metrics. GPU-accelerated local terminals do not remove the cost of bytes crossing a network or parser work inside a multiplexer.