anycomplextree
An anywidget that wraps react-complex-tree, giving you an interactive tree view inside Jupyter / JupyterLab / VS Code / Marimo notebooks with two-way state sync to Python.
Features
- Hierarchical tree rendering powered by
react-complex-tree - Two-way sync for selection, expansion, and focus
- Python-side validation of tree structure with clear error messages
- Single bundled ESM asset — no CDN fetches at runtime
Installation
uv add anycomplextree
Quick start
from anycomplextree import ComplexTree
tree = ComplexTree(
items={
"root": {
"isFolder": True,
"children": ["fruits", "meals"],
"data": "Root",
},
"fruits": {
"isFolder": True,
"children": ["apple", "orange", "banana"],
"data": "Fruits",
},
"apple": {"data": "Apple"},
"orange": {"data": "Orange"},
"banana": {"data": "Banana"},
"meals": {
"isFolder": True,
"children": ["pasta", "pizza"],
"data": "Meals",
},
"pasta": {"data": "Pasta"},
"pizza": {"data": "Pizza"},
},
root_item="root",
)
tree
The items model
Each entry in items represents one node. The dict key is the node's identifier — react-complex-tree calls it index. The key, the optional index field on the item, and the entries inside parent children arrays must all be the same string.
| field | type | description |
|---|---|---|
index |
str |
Unique identifier. Auto-filled from the dict key if omitted. |
data |
Any |
Per-item payload. By default used as the display title. |
isFolder |
bool |
Whether the item can have children. |
children |
list[str] |
IDs of child items (must exist as keys in items). |
Using UUIDs
Since the dict key is the identifier, just use UUID strings as keys:
import uuid
apple_id = str(uuid.uuid4())
fruits_id = str(uuid.uuid4())
tree = ComplexTree(
items={
fruits_id: {"isFolder": True, "children": [apple_id], "data": "Fruits"},
apple_id: {"data": "Apple"},
"root": {"isFolder": True, "children": [fruits_id], "data": "Root"},
},
)
tree.selected_items # -> [apple_id] after you click Apple
Traits
All traits are two-way synced between Python and the browser.
| trait | type | description |
|---|---|---|
items |
dict |
The tree contents (see above). |
root_item |
str |
Key of the root node. Defaults to "root". |
selected_items |
list[str] |
Currently selected item IDs. |
expanded_items |
list[str] |
Currently expanded folder IDs. |
focused_item |
str | None |
Currently focused item ID. |
Read, write, or observe them like any traitlet:
tree.expanded_items = ["root", "fruits"]
tree.selected_items = ["apple"]
tree.focused_item = "apple"
tree.observe(lambda change: print("selection:", change["new"]), names="selected_items")
Note: traitlets only fires change events when the trait is reassigned, not when mutated in place. Use
tree.selected_items = [...], nottree.selected_items.append(...).
Validation
items and root_item are validated on assignment. You'll get a TraitError with a clear message for:
- A dict key that doesn't match an explicit
indexfield - A
childrenentry that doesn't exist initems - A
root_itemthat isn't a key initems
Development
The widget bundle lives in js/ and builds to anycomplextree/static/.
# one-time install
cd js && npm install
# rebuild after JS changes
npm run build
# watch mode during development
npm run dev
On the Python side:
uv sync
uv run jupyter lab
License
MIT
Release files for anycomplextree 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| anycomplextree-0.1.1.tar.gz | 9.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| anycomplextree-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 77.4 kB
Release files / anycomplextree-0.1.1.tar.gz
| Download URL | anycomplextree-0.1.1.tar.gz |
|---|---|
| Size | 9.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6673d81c81341aa7f746d0028da06f95a4fa611881363cab1bcf9ee5bdb47022
|
|
BLAKE2b-256 checksum How to use checksums |
f8077df972a277adff5ce9bbce7a70a1ef3787daf6e407d6f6ea042acb1d7224
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 11, 2026.
Transparency logRelease files / anycomplextree-0.1.1-py3-none-any.whl
| Download URL | anycomplextree-0.1.1-py3-none-any.whl |
|---|---|
| Size | 68.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5e5e640cdf26198612bcf5fb4434050e43bf47d86818d8f19733410d8e91dd9b
|
|
BLAKE2b-256 checksum How to use checksums |
a49949a26b1986280a3af9e6ebad1a04096719b82ec7b144e8441530d96c141e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 11, 2026.
Transparency log