RepoPilotOpen in app →

Textualize/textual

The lean application framework for Python. Build sophisticated user interfaces with a simple Python API. Run your apps in the terminal and a web browser.

Healthy

Healthy across all four use cases

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 1d ago
  • 3 active contributors
  • MIT licensed
Show all 7 evidence items →
  • CI configured
  • Tests present
  • Small team — 3 contributors active in recent commits
  • Single-maintainer risk — top contributor 98% of recent commits

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/textualize/textual)](https://repopilot.app/r/textualize/textual)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/textualize/textual on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: Textualize/textual

Generated by RepoPilot · 2026-05-07 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/Textualize/textual shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across all four use cases

  • Last commit 1d ago
  • 3 active contributors
  • MIT licensed
  • CI configured
  • Tests present
  • ⚠ Small team — 3 contributors active in recent commits
  • ⚠ Single-maintainer risk — top contributor 98% of recent commits

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live Textualize/textual repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/Textualize/textual.

What it runs against: a local clone of Textualize/textual — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in Textualize/textual | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch main exists | Catches branch renames | | 4 | Last commit ≤ 31 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>Textualize/textual</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of Textualize/textual. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/Textualize/textual.git
#   cd textual
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of Textualize/textual and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "Textualize/textual(\\.git)?\\b" \\
  && ok "origin remote is Textualize/textual" \\
  || miss "origin remote is not Textualize/textual (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
  && ok "default branch main exists" \\
  || miss "default branch main no longer exists"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 31 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~1d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/Textualize/textual"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Textual is a Python framework for building sophisticated terminal and web-based user interfaces with a simple, modern API. It enables cross-platform TUI (text user interface) applications that run in both the terminal and web browsers without code changes, combining reactive patterns inspired by web frameworks with Python's simplicity. Monolithic framework structure: core rendering engine in /src/textual, widget library with components like Digits, DataTable, Tree in /src/textual/widgets, CSS-like styling system, async message-pump event loop, and DOM-like node hierarchy. Examples in /examples/ demonstrate patterns. Documentation mirrors API structure with /docs/api files for each major component (app.md, widgets.md, containers.md, events.md, etc.).

👥Who it's for

Python developers building terminal applications, DevOps tools, and CLI dashboards who want to create rich, interactive UIs without wrestling with ncurses or curses. Also appeals to web developers wanting to build terminal apps using web-like component and layout paradigms.

🌱Maturity & risk

Actively developed and production-ready. The codebase is substantial (~4.1M lines of Python) with comprehensive CI/CD workflows (GitHub Actions for black formatting, CodeQL security scanning, pythonpackage testing), extensive documentation in /docs/api, and organized issue templates. The framework has mature tooling (pre-commit hooks, coverage tracking via .coveragerc) and is actively maintained with regular updates.

Low-to-medium risk for new adopters. The codebase is large and complex, requiring understanding of async/await patterns and event-driven architecture. The framework is still evolving (indicated by detailed CHANGELOG.md), which could bring breaking changes; however, the presence of a comprehensive testing framework and CI setup mitigates integration risk. Primary risk is learning curve for developers unfamiliar with TUI frameworks or reactive programming patterns.

Active areas of work

Active development on widget gallery expansion, testing framework improvements, and cross-platform browser rendering. The presence of /docs/api files for recent features (lazy.md, fuzzy_matcher.md, highlight.md) and comprehensive CI workflows (.github/workflows/) indicates ongoing feature development and quality assurance. CHANGELOG.md tracks recent changes and milestones.

🚀Get running

git clone https://github.com/Textualize/textual.git
cd textual
pip install -e .
python examples/app.py

The Makefile provides development tasks; inspect it with make help for testing and linting commands.

Daily commands: Terminal: python examples/app.py or any script with if __name__ == "__main__": app = MyApp(); app.run(). Development: make test (inferred from pythonpackage.yml) runs pytest, make format runs black. See Makefile for all targets.

🗺️Map of the codebase

🛠️How to make changes

New widgets: Add class to /src/textual/widgets/ inheriting from Widget, implement render() or compose(). New events: Add Message subclass to /src/textual/message.py, define handler as on_<message_name>(). Styling: Modify CSS parsing in /src/textual/css/ or add properties to style system. Testing: Add tests in /tests/ mirroring source structure; framework has advanced testing utilities in /src/textual/pilot.py. Docs: Add .md file to /docs/api/ following existing API doc patterns.

🪤Traps & gotchas

Async requirement: Framework is asyncio-based; blocking operations will freeze the TUI. Must use async/await or run_worker() for long tasks. CSS differences: Terminal CSS doesn't support all web CSS properties; consult /docs/api for supported rules. Terminal-specific rendering: Colors, fonts, and exact layout vary by terminal capabilities; test on target terminals. Event loop integration: Cannot easily embed Textual apps in other async frameworks without careful event loop management. Relative imports in examples: Examples use local imports; production apps should install via pip and use absolute imports.

💡Concepts to learn

  • Message Passing Architecture — Textual's entire event system is built on async message passing via message_pump.py; understanding how Message subclasses route to on_* handlers is essential to building interactive widgets
  • Reactive Attributes — Textual widgets use reactive properties (similar to Vue.js) that automatically trigger re-renders when changed; this is how state flows through the app without manual invalidation
  • Compose Pattern (Component Trees) — Apps build UIs by yielding widgets in compose() methods, creating a declarative DOM-like tree; this pattern is borrowed from web frameworks and is core to Textual's design philosophy
  • Terminal CSS Styling — Textual uses a CSS-like syntax for styling (colors, layout, borders) parsed and applied at runtime; unlike web CSS, it's terminal-constrained, requiring knowledge of what properties are supported
  • Async/Await Integration — The framework is built on asyncio; developers must use async methods and run_worker() for background tasks to avoid blocking the event loop and freezing the TUI
  • Virtual Terminal Rendering — Textual abstracts terminal output through a virtual terminal layer (in /src/textual/) that supports both terminal and web backends; understanding this abstraction explains cross-platform portability
  • Textualize/rich — Companion library by same author; Textual uses Rich for rendering rich text, tables, and syntax highlighting in widgets
  • prompt-toolkit/python-prompt-toolkit — Alternative TUI framework for Python; more mature for simple CLI apps but less suitable for complex UIs than Textual
  • willmcgugan/rich-cli — Example CLI built with Rich; demonstrates patterns for building terminal tools, complementary to Textual for dashboard-style apps
  • Cormanz/smartGPT — Real-world Textual application example showing production TUI design patterns and async integration with external APIs
  • cursive-rs/cursive — Rust equivalent providing inspiration for Textual's reactive widget model and layout system from the Rust TUI ecosystem

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive test coverage for renderables API module

The docs/api/renderables.md exists but there's no corresponding test file visible in the structure. Given that Textual is a UI framework where renderables are core to displaying content, this module likely lacks unit tests. Adding tests for renderables would ensure rendering logic is robust across terminal and web backends.

  • [ ] Create tests/test_renderables.py with unit tests for renderables module
  • [ ] Test rendering output for common widget types (Text, Static, etc.)
  • [ ] Add edge case tests for empty renderables, very long content, and special characters
  • [ ] Run coverage reports to ensure >85% coverage for renderables module

Implement missing API documentation for lazy-loading widgets

docs/api/lazy.md exists in the file structure but the lazy-loading feature is critical for performance in terminal UIs. There's likely missing practical examples showing how to implement lazy loading in custom widgets, and no corresponding guide in the docs/ folder explaining the pattern.

  • [ ] Audit docs/api/lazy.md to identify missing examples or unclear sections
  • [ ] Create docs/guide/lazy_loading.md with practical patterns for lazy rendering
  • [ ] Add 2-3 example apps demonstrating lazy loading with real use cases (large lists, dynamic content)
  • [ ] Cross-reference the guide from docs/api/index.md

Add GitHub Actions workflow for web backend testing

The .github/workflows/ directory has pythonpackage.yml and codeql.yml, but no dedicated workflow for testing the web browser backend. Since Textual supports running apps in both terminal and web browser, there should be a separate CI step validating web rendering and WebSocket communication.

  • [ ] Create .github/workflows/test_web_backend.yml GitHub Action
  • [ ] Add steps to run tests with WEB_BACKEND environment variable enabled
  • [ ] Include browser compatibility checks (Chrome, Firefox headless) using Playwright or Selenium
  • [ ] Add performance benchmarks for web rendering to catch regressions

🌿Good first issues

  • Add comprehensive docstring examples to /src/textual/widgets/ files lacking them (e.g., Button, Input, Select) following the pattern in DataTable.py — helps users learn by example.
  • Create missing widget API documentation: scan /src/textual/widgets/ for new widgets without corresponding /docs/api/*.md files and generate them from docstrings using the existing API doc template.
  • Expand test coverage for /src/textual/css/parse.py edge cases (e.g., malformed CSS, unsupported properties) by adding test cases to /tests/test_css.py — improves robustness.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • a066560 — Merge pull request #6522 from Textualize/fence-offsets (willmcgugan)
  • 551d4ff — snapshots (willmcgugan)
  • 21c1776 — offsets fix (willmcgugan)
  • 5fdea0d — Merge pull request #6513 from Textualize/ansi-themes (willmcgugan)
  • 992eb86 — superfluous code (willmcgugan)
  • eebf3fc — style tweak (willmcgugan)
  • a82be18 — theme tweak (willmcgugan)
  • 12ef258 — bump (willmcgugan)
  • f4b774a — changelog (willmcgugan)
  • 978f86a — simplify (willmcgugan)

🔒Security observations

The Textual repository demonstrates a reasonable security posture with established governance (CODE_OF_CONDUCT.md, CONTRIBUTING.md), CI/CD workflows (GitHub Actions), and code analysis tools configured (.deepsource.toml, .coveragerc). However, without access to the actual dependency specifications and source code, full security assessment is limited. The main concerns are: (1) lack of visible dependency lock files, (2) missing security policy documentation, and (3) inability to verify dependencies for known vulnerabilities. As a UI framework project, it should have minimal injection risks compared to backend services, but developers should ensure proper input validation in terminal rendering components. Recommend implementing automated security scanning, dependency auditing, and maintaining a public security policy.

  • Low · Missing package lock file visibility — Repository root. No lock file (requirements.lock, poetry.lock, Pipfile.lock, etc.) was provided in the file structure. While not directly a vulnerability, this makes dependency auditing difficult and could lead to supply chain risks if dependencies are not pinned to specific versions. Fix: Ensure all dependencies are locked to specific versions using tools like pip-tools, Poetry, or Pipenv. Include lock files in version control and CI/CD pipelines.
  • Low · Missing security policy documentation — Repository root. No SECURITY.md file is visible in the repository structure. This could delay vulnerability reporting and response coordination. Fix: Create a SECURITY.md file following GitHub's security policy guidelines (https://docs.github.com/en/code-security/getting-started/adding-a-security-policy-to-your-repository) with instructions for responsible disclosure.
  • Low · Limited visibility into dependencies — setup.py, setup.cfg, pyproject.toml, or requirements.txt (not provided). The provided dependency/package file content is empty. Cannot assess for known vulnerable package versions, outdated dependencies, or supply chain risks. Fix: Provide dependency manifests and run regular security audits using tools like 'pip-audit', 'safety', or 'bandit'. Integrate dependency checking into CI/CD pipelines.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · Textualize/textual — RepoPilot