Catalog
ui.Catalog is a component for things to install (agents from a registry, MCP servers, skills): a search box over a scrolling list where each row shows the name, version and description, and on the right Install, Installed or Update to a newer version. items are dicts or objects with id, name and optional version, description, installed (a bool) and update (the newer version). Install and Update report on_install with the item’s id in event.value; while busy holds an id, that row shows a spinner and the other Install buttons are disabled. height is the list’s height.
ui.Catalog · Category: AI agents · Full reference →


pz.container(ui.Catalog(items=[ {"id": "gemini", "name": "Gemini CLI", "version": "0.64.0", "description": "A coding agent for the terminal.", "installed": True, "update": "0.65.0"}, {"id": "codex", "name": "Codex CLI", "version": "0.40.0", "description": "Writes and runs code for you."}, {"id": "goose", "name": "Goose", "version": "1.9.0", "description": "An open source agent.", "installed": True}, {"id": "opencode", "name": "OpenCode", "version": "0.12.1", "description": "An agent that edits whole projects."},], busy="codex", height=220, on_install=on_install), width=460)When to use
Section titled “When to use”- To add agents, MCP servers, skills or plugins from a list, inside a dialog or a settings page.
import pyzed as pzfrom pyzed import ui
class Agents(pz.App): def __init__(self): self.registry = load_registry() # [{"id", "name", "version", "description", "installed"}, ...] self.installing = ""
def view(self): return ui.Catalog(items=self.registry, busy=self.installing, on_install=lambda e: self.install(e.value))Variants
Section titled “Variants”While one item installs, the other Install buttons are disabled.


Code
pz.container(ui.Catalog(items=[{"id": "goose", "name": "Goose", "version": "1.9.0", "description": "An open source agent."}], height=50), width=380)

installed=TrueCode
pz.container(ui.Catalog(items=[{"id": "goose", "name": "Goose", "version": "1.9.0", "description": "An open source agent.", "installed": True}], height=50), width=380)

update="2.0.0"Code
pz.container(ui.Catalog(items=[{"id": "goose", "name": "Goose", "version": "1.9.0", "description": "An open source agent.", "installed": True, "update": "2.0.0"}], height=50), width=380)

busy="goose"Code
pz.container(ui.Catalog(items=[ {"id": "goose", "name": "Goose", "version": "1.9.0", "description": "An open source agent."}, {"id": "codex", "name": "Codex CLI", "version": "0.40.0", "description": "Writes and runs code."},], busy="goose", height=100), width=380)

Code
pz.container(ui.Catalog(items=[{"id": "goose", "name": "Goose"}], height=50), width=380)Events
Section titled “Events”Catalog has 1 event of its own, listed below. Like every widget it also has the 18 common events (click, double_click, right_click, mouse_enter, drag, key_down, resize…), attached with .on(...): see Events and Events of every component.
It is a pz.Component, so it also raises on_mount and on_unmount, and its handlers get a pz.Event whose value holds the data.
| Event | Handler gets | When |
|---|---|---|
on_install |
(event) |
Install or Update was pressed (event.value is the id). |
Handler gets is what Pyzed passes to your callable: () means nothing. Any handler can also be a message (a value that is not callable), which goes to update().
Handling the events
Section titled “Handling the events”The same events written three ways. Imperative: methods of the app change self. Declarative: the handlers produce messages and one update() decides. Functional: a pure function draws an immutable state and asks for changes with set_state(...). See Imperative, declarative and functional.
class Agents(pz.App): def __init__(self): self.items = [{"id": "goose", "name": "Goose", "version": "1.9.0"}, {"id": "codex", "name": "Codex CLI", "version": "0.40.0"}] self.installing = ""
def install(self, ident): # on_install: event.value is the item's id self.installing = ident
def view(self): return ui.Catalog(items=self.items, busy=self.installing, height=200, on_install=lambda e: self.install(e.value))from dataclasses import dataclass
@dataclassclass Install: ident: str
class Agents(pz.App): items = [{"id": "goose", "name": "Goose", "version": "1.9.0"}] installing = ""
def update(self, message): match message: case Install(ident): self.installing = ident
def view(self): return ui.Catalog(items=self.items, busy=self.installing, height=200, on_install=lambda e: Install(e.value))from dataclasses import dataclass
@dataclass(frozen=True)class State: installed: frozenset = frozenset()
ITEMS = [{"id": "goose", "name": "Goose", "version": "1.9.0"}]
def view(state, set_state): items = [{**i, "installed": i["id"] in state.installed} for i in ITEMS] return ui.Catalog(items=items, height=200, on_install=lambda e: set_state(installed=state.installed | {e.value}))Properties and API
Section titled “Properties and API”ui.Catalog
Section titled “ui.Catalog”class CatalogA component for things to install with a search box (agents from a registry, servers, skills…): each row shows Install, Installed or Update to a newer version.
| Property | Default | Description |
|---|---|---|
items |
None |
Dicts or objects with id, name and optional version, description, installed and update. |
busy |
"" |
The id being installed (a spinner). |
height |
320 |
Height of the list. |
theme |
None |
Theme. |
Events: on_install (events).