Library support

Note

This page is generated from src/textual_wasm/ecosystem.py by textual-wasm libraries, and a test fails when the committed copy drifts from it. Every verdict came from installing the distribution into a real Pyodide and mounting its widgets in a running Textual app.

Library support

37 Textual add-on libraries, probed inside Pyodide 314.0.6 against Textual 8.2.8 on 2026-09-12. 23 of them can be shipped to a browser today.

Measured in four tiers, because no single one of them is a verdict: micropip install, import, this project’s own static scan, and then the tier that decides a port - constructing every Widget subclass the library defines and mounting it in a running Textual app under the capture driver.

Two results before the table. On the first run of this survey the static scan produced no findings at all, across every library - which was a gap in the rules, not a clean bill of health. The two failures the runtime tier had already found showed where, and both now have registry entries that fire on the exact lines responsible. And the most-downloaded package here, textual-serve, installs and imports cleanly while being the one thing on this page you definitely should not use with it.

Warning

textual[syntax] does not install under Pyodide 314.0.6. Textual’s syntax extra needs tree-sitter>=0.25.0; Pyodide bundles 0.23.2 as a native wheel, which cannot be fetched from PyPI at another version. Found by way of textual-textarea, but not specific to it. micropip’s message - “can’t find a pure Python wheel for tree-sitter” - is misleading: it exists, one minor version too old.

A plain TextArea was never affected. For highlighting the whole gap is one class, so textual_wasm.treesitter supplies it and TextArea.code_editor(language=...) works for the three grammars Pyodide bundles - python, go and java. Measured in Chromium and Firefox: 14 and 13 distinct colours painted, typing still reparsing, no console errors. The other twelve languages in the extra have no Pyodide recipe at all.

Ordered by monthly PyPI downloads within each group, so the rows people are most likely to want come first. Run textual-wasm doctor <module:App> -r <dist> to check your own dependencies the same way.

Works

Installed, imported, and at least one of the library’s own widgets mounted and rendered inside a running Textual application under Pyodide. This is the only row class that means works in a browser - the three tiers below it are each a weaker claim, and they fail for unrelated reasons.

Distribution

Downloads/month

What it is

What the probe saw

textual-autocomplete 4.0.6

675,128

dropdown autocompletion for Input

Input plus AutoComplete(target='#id', candidates=[...]) mounted and rendered 7796 characters. Pure Python, depends only on textual and typing-extensions. Mounted: AutoComplete, PathAutoComplete. PathAutoComplete completes against a filesystem, which in a browser is MEMFS - it will complete over whatever the build shipped, not the user’s disk.

textual-image 0.13.2

254,448

images via Sixel and the Terminal Graphics Protocol

textual_image.widget.Image(pil_image) - the backend-selecting widget - mounted and rendered 9115 characters. Pillow is bundled by Pyodide, so it resolves. The doctor now reports three findings in its source: fcntl.ioctl and two termios.tcsetattr calls in _posix.py, which is the terminal interrogation behind the hang below. Mounted: Image. Only the auto-selecting widget was verified. A probe that imported every submodule of the package hung instead of failing, so one of the backend modules blocks on import under this driver - unsurprising for a library whose job is to ask the terminal what protocols it supports. Import textual_image.widget.Image and let it choose; do not import a specific backend.

textual-plotext 1.0.1

207,180

Plotext charts as a Textual widget

All 6 widgets constructed, mounted and rendered. No native dependency. Mounted: PlotextPlot.

textual-universal-directorytree 1.7.0

108,980

DirectoryTree over local and remote filesystems

UniversalDirectoryTree('/') mounted and rendered 11143 characters over Pyodide’s own in-memory filesystem. Mounted: UniversalDirectoryTree. Local paths browse MEMFS, which holds only what the build shipped. Its remote backends (s3://, gs://, github://) are the interesting use and were not measured: each needs network access and is therefore subject to CORS.

textual-fastdatatable 0.19.2

57,158

a DataTable backed by Arrow, for large tables

Installed and its widget mounted and rendered. Mounted: DataTable. It can use pyarrow, which Pyodide bundles at 22.0.0 and no other version. A pin excluding that is a version_conflict the doctor reports before you build.

textual-fspicker 1.0.1

50,738

file open/save and directory picker dialogs

10 of 11 widgets mounted and rendered, including FileOpen, FileSave and SelectDirectory. The eleventh, FileFilter, needs arguments. Mounted: FileOpen, FileSave, SelectDirectory, DirectoryNavigation. It picks paths in Pyodide’s filesystem, not the user’s. For a real file from the user’s machine you need the browser’s own picker; for handing one back, App.deliver_binary and the page’s deliverFile capability.

textual-hires-canvas 0.14.0

31,124

a high-resolution drawing canvas

Installed and its widget mounted and rendered. Mounted: Canvas.

textual-plot 0.10.1

31,046

a native plotting widget with zoom and pan

All 7 widgets constructed, mounted and rendered. Mounted: PlotWidget.

textual-imageview 0.1.1

14,601

an image viewer widget

ImageViewer(Image.new('RGB', (8, 8))) mounted and rendered 10923 characters. Pillow is bundled by Pyodide, so the dependency resolves. Mounted: ImageViewer. Renders with half-block characters, so it needs no terminal graphics protocol.

textual-datepicker 0.2.4

10,488

a date picker

6 of 9 widgets mounted and rendered; the other 3 need constructor arguments. Mounted: DatePicker, MonthControl.

zandev-textual-widgets 1.2.0

4,600

an assorted widget collection

20 of 28 widgets mounted and rendered; 6 need constructor arguments and 2 are abstract bases that cannot be constructed anywhere.

textual-slider 0.2.0

3,197

a slider

Slider(min=0, max=10) mounted and rendered 9172 characters. Mounted: Slider.

textual-colorpicker 0.1.0

2,396

a colour picker

All 8 widgets mounted and rendered. Mounted: ColorPicker.

textual-timepiece 0.7.0

1,166

date, time, duration and timeline widgets

42 of 50 widgets mounted and rendered. Its Textual requirement is >=6.0.0,<9.0.0, which includes the pinned 8.2.8 - the only library here with a cap that does. Mounted: DatePicker, DurationInput, TimeInput.

textual-canvas 1.1.0

421

a character-cell drawing canvas

Canvas(16, 8) mounted and rendered 5467 characters. Mounted: Canvas.

textual-tags 0.3.3

70

a tag-entry widget

Tags(tag_values=[...]) mounted and rendered 6459 characters. Mounted: Tags.

textual-countdown 1.0.0

48

a visual countdown timer

Its widget mounted and rendered. Mounted: Countdown.

textual-astview 0.8.0

25

a Python AST explorer

ASTView(module=path) mounted and rendered 9613 characters over MEMFS. Mounted: ASTView.

textual-dad-joke 0.0.1

8

a dad joke widget, seriously

Its widget mounted and rendered. Mounted: DadJoke. It fetches from icanhazdadjoke.com, so in a browser it is subject to CORS - the mount succeeding says nothing about the request succeeding.

Installs, not exercised

Installs and imports cleanly, but nothing could be mounted automatically: either it defines no Textual widget at all (a Rich renderable, a stylesheet) or every widget it defines needs constructor arguments a generic probe cannot invent. Absence of evidence, listed as such and not rounded up to working.

Distribution

Downloads/month

What it is

What the probe saw

rich-pixels 3.0.1

93,903

images as Rich renderables

Installs and imports. Defines no Textual widget - it is a Rich renderable, so it reaches a Textual app through Static(Pixels.from_image(...)). Wrap it in a Static; there is nothing browser-specific about it.

tuilwindcss 0.1.0

8

Tailwind-like utility classes for Textual CSS

Installs and imports. Ships stylesheets, not widgets, so nothing to mount.

textual-pdf 0.1.7

7

a PDF preview widget, on top of textual-image

Installs and imports; PDFViewer needs a path and was not exercised. It renders through textual-image, whose own verdict applies.

textual-select 0.3.4

-

a select/dropdown with a searchable list

Installs and imports. All four of its widgets need constructor arguments (items, list_mount, select_list), so none was exercised.

Caps Textual below this build’s

Requires a Textual older than the 8.2.8 this project pins. The trap is that micropip.install(name) on its own succeeds - it quietly resolves Textual down to a version the cap allows, and you discover the downgrade later as missing widgets or changed APIs. None of this is a WASM constraint: the same cap binds a terminal install identically.

Distribution

Downloads/month

What it is

What the probe saw

textual-slidecontainer 1.0.0

1,587

a sliding or hidden menu-bar container

Requires textual>=3.0.0,<6.0.0. micropip.install alone succeeds by fetching an older Textual; asked to coexist with 8.2.8 it refuses. Not a WASM constraint - the cap binds a terminal install identically. Either pin your app to a Textual it allows, or wait for the library to widen it.

textual-coloromatic 1.0.1

1,475

animated colour effects and tiled backgrounds

Requires textual>=3.0.0,<6.0.0; refuses to coexist with 8.2.8. Same as textual-slidecontainer: a version cap, not a browser problem.

textual-pyfiglet 1.1.0

1,384

Pyfiglet banner text with colour and animation

Requires textual>=3.0.0,<6.0.0; refuses to coexist with 8.2.8. Its own dependency, pyfiglet, is pure Python and installs under Pyodide cleanly - so calling it directly and rendering into a Static is a working route that does not need this wrapper at all.

textual-window 0.8.1

78

floating, draggable windows and a window manager

Requires textual>=5.1.0,<6.0.0; refuses to coexist with 8.2.8. The narrowest cap in the survey - one minor version wide - so it is the most likely of these to be widened by its next release. Pin Textual to 5.x if you need it now.

textual-spinbox 0.8.2

13

a spinbox

Requires textual>=1.0.0,<2.1.3; refuses to coexist with 8.2.8. Seven majors behind, so pinning Textual down to satisfy it costs more than the widget is worth. An Input with type='integer' plus two key bindings is the shorter route.

Cannot be installed

The install fails under Pyodide. Read the middle column before assuming the reason is WebAssembly - three of these are ordinary dependency pins that would fail anywhere, and only the first is about a missing native build.

Distribution

Downloads/month

What it is

What the probe saw

textual-textarea

48,465

a full text editor widget, from the Harlequin project

Depends on textual[syntax], and that extra requires tree-sitter>=0.25.0 while Pyodide bundles 0.23.2 as a native wheel - so it cannot be fetched from PyPI at any version. Measured directly: micropip.install('textual[syntax]') fails, micropip.install('textual') succeeds. This is the widest-reaching finding in the survey and it is not really about this library: textual[syntax] does not install under Pyodide 314.0.6, so Textual’s own TextArea syntax highlighting is unavailable too. micropip’s message - “can’t find a pure Python wheel for tree-sitter” - is misleading: it exists, one minor version too old.

textual-terminal 0.3.0

2,695

embed another terminal application inside Textual

Installs, then fails to import: cannot import name 'DEFAULT_COLORS' from 'textual.app', a symbol current Textual no longer has. Unfixable here even once that import is repaired: it needs a pty and a child process, and a browser tab has neither. subprocess raises and os.system silently does nothing - see the porting matrix.

textual-filedrop

72

a drag-and-drop FileDrop widget

Requires markdown-it-py>=2.1.0,<3.0.0, which conflicts with the 4.2.0 that current Textual itself installs. A stale transitive pin, not a browser constraint. Drag-and-drop into a page is a browser API anyway, so a terminal FileDrop is not the right mechanism here.

textual-pandas

27

display Pandas dataframes in Textual

Pins pandas<=2.2.3; Pyodide bundles pandas 3.0.2 as a native wheel. The pin excludes the only version obtainable, so the install fails outright. The textbook version-conflict case, and exactly what textual-wasm build now refuses before writing a site. Use textual-fastdatatable, or read the frame yourself into a DataTable.

textual-qrcode 0.2.0

11

a QR code widget

Pins textual==0.10.1 exactly, and its import fails against anything current. Generates codes by calling qrenco.de over HTTP. Both halves would need fixing: the exact pin, and then CORS on qrenco.de.

Not packaged

No distribution exists to install. micropip fetches from PyPI or a URL and has no path to a git repository, so the fix is to vendor the source into your own package - where the build ships it like any other module.

Distribution

Downloads/month

What it is

What the probe saw

textual-effects

-

transition effects for widgets

micropip cannot fetch metadata for it - there is no PyPI distribution, only a repository. micropip installs from PyPI or a URL, never from git. Vendor the source into your own package, where the build will ship it like any other module.

Not something you ship to a page

Installs fine and is still the wrong tool here: a development dependency, or the server-side architecture this project is the alternative to. Listed because between them they are the most-downloaded packages in the survey, so “it installed” would otherwise read as an endorsement.

Distribution

Downloads/month

What it is

What the probe saw

textual-serve 1.1.3

1,128,421

serve a Textual app over HTTP from a Python server

Installs and imports under Pyodide, which is precisely why the install tier alone is not a verdict. The doctor now flags web.run_app at server.py:229 - and that call is the divergent kind: silently fine under Node, OSError 138 in a browser, so a Node-only check would have passed it. It is the other architecture: a server runs Python and streams the terminal to a browser. This project runs Python in the browser and needs no server. Pick one - there is nothing to compose. It is also the most-downloaded package in this survey, so the comparison is the one most people want.

textual-dev 1.8.0

397,962

Textual’s development tools and console

Installs and imports under Pyodide. A development dependency: run it on your machine against the terminal build. Its console talks to a devtools server over a socket, which a page cannot open.

pytest-textual-snapshot 1.1.0

281,667

snapshot testing for Textual apps

Installs and imports under Pyodide. Test on the terminal build, where it runs under pytest normally. For checking that the browser renders identically, textual-wasm check compares the runtimes cell by cell.

What this does not tell you

  • WebKit is unmeasured. It would not launch on the machine that ran these probes, so every verdict is Chromium and Firefox. See browser support.

  • Mounting is not using. A widget that mounts and renders can still fail on an interaction nobody drove. Libraries that reach the network (textual-qrcode, textual-dad-joke) or a real filesystem (textual-fspicker, textual-universal-directorytree) were mounted, not exercised end to end - and in a browser those two are exactly where CORS and MEMFS bite.

  • Versions move. Every row is one version on one date. A textual_cap verdict in particular is the most likely to be fixed by the library’s next release.