Skip to main content
Ctrl+K
bootstack is in pre-release — the API may still change before 1.0. Install with pip install --pre bootstack.
bootstack - Home bootstack - Home
  • User Guide
  • Widgets
  • API Reference
  • Production
  • GitHub
  • User Guide
  • Widgets
  • API Reference
  • Production
  • GitHub

Application

  • App
  • AppShell
  • Window

Actions

  • Button
  • ButtonGroup

Inputs

  • TextField
  • PasswordField
  • NumberField
  • Slider
  • RangeSlider
  • PathField
  • SpinnerField
  • TextArea
  • CodeEditor
  • DateField
  • TimeField

Selection

  • Checkbox
  • Select
  • Switch
  • ToggleButton
  • RadioGroup
  • ToggleGroup
  • SelectButton
  • Calendar

Data Display

  • Label
  • Badge
  • ProgressBar
  • Gauge
  • ListView
  • DataTable
  • Tree

Media

  • Picture
  • Gallery
  • Carousel
  • Avatar

Layout

  • Separator
  • Card
  • GroupBox
  • VStack
  • HStack
  • Grid
  • Accordion
  • ScrollView
  • SplitView

Chrome bars and menus

  • Menubar
  • CommandBar
  • StatusBar
  • MenuButton
  • ContextMenu

Navigation

  • PageStack
  • Tabs

Overlays

  • Tooltip
  • Toasts & Notifications

Dialogs

  • Message Dialogs
  • Input Dialogs
  • Color Dialog
  • Font Dialog
  • Filter Dialog
  • Dialog
  • Form Dialog

Forms

  • Form
  • Widgets
  • Gallery

Gallery#

A scrollable, selectable grid of image thumbnails. Gallery displays a collection of image records as a reflowing grid, recycling tiles so it stays efficient for large collections. It is record-native — populate it with items= or a data_source= and read the chosen tile(s) back through selection, the same model ListView, DataTable, and Tree share.

Gallery — light theme Gallery — dark theme

Usage#

Populating the grid#

Each record names the image to show through image_field (an Image, a file path, or a PIL image) and, optionally, a caption through caption_field. Other keys ride along in the record bag:

items = [
    {"id": 1, "photo": "beach.jpg",    "name": "Beach"},
    {"id": 2, "photo": "sunset.jpg",   "name": "Sunset"},
    {"id": 3, "photo": "mountain.jpg", "name": "Mountain"},
]

bs.Gallery(items=items, image_field="photo", caption_field="name")

For database- or API-backed data, pass a data_source= instead of items=.

Reflow and tile size#

By default the grid reflows to fit the available width — the column count changes as the window resizes. Set tile_size to size the thumbnails, or fix the column count with columns=:

bs.Gallery(items=items, tile_size=(160, 160))            # auto columns
bs.Gallery(items=items, columns=4, tile_size=(120, 120))  # fixed 4-up

fit controls how each image is scaled into its tile — the same modes as Picture ('cover' by default), and corner_radius rounds the thumbnails.

Selection#

Set selection_mode to 'single' or 'multi' to let tiles be selected; selected tiles show an accent highlight ring. Read the selection — the full record bag — through selection:

gallery = bs.Gallery(items=items, selection_mode="multi", accent="primary")
gallery.on_select(lambda e: print(gallery.selection))   # list of record dicts
Gallery selection — light theme Gallery selection — dark theme

In 'single' mode selection is a record dict (or None); in 'multi' mode it is a list of record dicts. Drive selection from code with select_items, deselect_items, select_all, and clear_selection.

Clicks and activation#

on_item_click fires when a tile is clicked; on_item_activate fires on double-click — the natural place to open a full-size viewer or detail page:

gallery.on_item_click(lambda r: print("clicked", r["name"]))
gallery.on_item_activate(lambda r: open_viewer(r))

Widget sizing#

All widgets accept self-placement kwargs via **kwargs. The parent container determines which options apply — stack-based parents use stack kwargs, grid-based parents use grid kwargs. Unrecognised keys are silently ignored.

Stack#

Used inside VStack, HStack, App, and other stack containers.

fill

Fill direction: 'x', 'y', 'both', or 'none'.

expand

Grow to consume extra space in the parent. True or False.

anchor

Alignment when the widget does not fill the available slot: 'n', 's', 'e', 'w', 'center', 'nw', etc.

margin

External spacing in pixels. Accepts an integer (equal on all sides), a 2-tuple (horizontal, vertical), or a 4-tuple (left, top, right, bottom).

margin_x

Horizontal external spacing (left and right). Accepts an integer or a 2-tuple (left, right) for asymmetric spacing. Overrides the horizontal component of margin=.

margin_y

Vertical external spacing (top and bottom). Accepts an integer or a 2-tuple (top, bottom) for asymmetric spacing. Overrides the vertical component of margin=.

Grid#

Used inside a Grid container.

row / column

Zero-based row and column indices.

rowspan / columnspan

Number of rows or columns to span.

sticky

Alignment and fill within the grid cell. Any combination of 'n', 's', 'e', 'w' — e.g. 'ew' stretches horizontally, 'nsew' fills the entire cell.

margin

External spacing in pixels. Accepts an integer, a 2-tuple (horizontal, vertical), or a 4-tuple (left, top, right, bottom).

margin_x

Horizontal external spacing. Accepts an integer or (left, right).

margin_y

Vertical external spacing. Accepts an integer or (top, bottom).

See also#

  • Picture — the single-image display widget each tile is built from.

  • Image — the image source handle.

  • ListView — the single-column record list with the same selection model.

API#

The complete reference for Gallery lives on the Widgets API page. At a glance:

Gallery

A scrollable, selectable grid of image thumbnails.

Full Example#

 1
 2_DIR = tempfile.gettempdir()
 3_PALETTE = [
 4    (220, 90, 90), (90, 170, 110), (90, 130, 220), (210, 160, 70),
 5    (160, 110, 200), (80, 180, 200), (200, 110, 160), (120, 140, 90),
 6]
 7
 8
 9def _thumb(i: int) -> str:
10    """A gradient thumbnail with an antialiased numbered disc."""
11    path = os.path.join(_DIR, f"bs_gallery_{i}.png")
12    w, h = 240, 180
13    base = _PALETTE[i % len(_PALETTE)]
14    img = PILImage.new("RGB", (w, h))
15    px = img.load()
16    for y in range(h):
17        for x in range(w):
18            px[x, y] = (
19                int(base[0] * (0.5 + 0.5 * x / w)),
20                int(base[1] * (0.5 + 0.5 * y / h)),
21                int(base[2] * (0.6 + 0.4 * x / w)),
22            )
23    ss = 4
24    layer = PILImage.new("RGBA", (w * ss, h * ss), (0, 0, 0, 0))
25    d = ImageDraw.Draw(layer)
26    r = 46 * ss
27    cx, cy = w * ss // 2, h * ss // 2
28    d.ellipse([cx - r, cy - r, cx + r, cy + r], fill=(255, 255, 255, 230))
29    layer = layer.resize((w, h), Resampling.LANCZOS)
30    img = img.convert("RGBA")
31    img.alpha_composite(layer)
32    img.convert("RGB").save(path)
33    return path
34
35
36items = [
37    {"id": i, "image": _thumb(i), "name": f"Photo {i + 1}"}
38    for i in range(18)
39]
40
41with bs.App(title="Gallery", size=(620, 480), padding=16, gap=8) as app:
42    bs.Label("Image gallery — click to select, double-click to open", font="heading-md")
43    gallery = bs.Gallery(
44        items=items,
45        image_field="image",
46        caption_field="name",
47        columns="auto",
48        tile_size=(140, 110),
49        corner_radius=10,
50        selection_mode="multi",
51        fill="both",
52        expand=True,
53    )
54    gallery.on_item_activate(lambda r: print("open:", r["name"]))
55
56app.run()

previous

Picture

next

Carousel

On this page
  • Usage
    • Populating the grid
    • Reflow and tile size
    • Selection
    • Clicks and activation
    • Widget sizing
      • Stack
      • Grid
  • See also
  • API
  • Full Example

© Copyright 2026, Israel Dryer.

Created using Sphinx 9.1.0.

Built with the PyData Sphinx Theme 0.19.0.