Grid#

A container that arranges children in rows and columns. Children are auto-placed left-to-right, top-to-bottom by default. Column and row sizes are defined with columns= and rows=; omitting them lets the grid size itself to fit its content.

Grid — light theme Grid — dark theme

Usage#

Column definitions#

columns= accepts an integer or a list of per-column sizes:

  • Integer (shorthand) — creates that many equal-weight columns. columns=3 is equivalent to columns=[1, 1, 1].

  • Integer weight in a list — relative share of available space (e.g. 1, 2). Weight 0 is equivalent to 'auto' — prefer 'auto' for clarity.

  • 'auto' — sized to the widest child in that column; does not grow.

  • 'Npx' — fixed pixel width (e.g. '120px').

# Three equal columns — integer shorthand
bs.Grid(columns=3)

# Same, written explicitly
bs.Grid(columns=[1, 1, 1])

# Label column sizes to content; field column takes the rest
bs.Grid(columns=["auto", 1])

# Fixed sidebar, flexible content, fixed panel
bs.Grid(columns=["120px", 1, "80px"])
Grid column definitions — light theme Grid column definitions — dark theme

Row definitions#

rows= follows the same format as columns=. When omitted, rows are added automatically as children are placed.

  • Integer (shorthand) — creates that many equal-weight rows. rows=3 is equivalent to rows=[1, 1, 1].

  • Integer weight in a list — relative share of available vertical space.

  • 'auto' — sized to the tallest child in that row; does not grow.

  • 'Npx' — fixed pixel height (e.g. '80px').

# Three equal rows — integer shorthand
bs.Grid(rows=3)

# Mixed: two auto-height rows, one that fills remaining space
bs.Grid(rows=["auto", 1, "auto"])

Gap#

gap= sets spacing between cells. An integer applies to both axes; a 2-tuple (col_gap, row_gap) sets them independently.

bs.Grid(columns=[1, 1], gap=8)            # 8 px between all cells
bs.Grid(columns=[1, 1], gap=(32, 8))      # 32 px between columns, 8 px between rows
Grid gap — light theme Grid gap — dark theme

Child alignment — sticky_items#

sticky_items= controls how children fill their cell. Pass any combination of 'n', 's', 'e', 'w':

  • 'ew' — stretch horizontally, natural height (most common for forms)

  • 'nsew' — stretch to fill the entire cell

  • '' — center in cell at natural size

Individual children can override the default with their own sticky=.

# stretch horizontally — natural height
with bs.Grid(columns=[1, 1], gap=8, sticky_items="ew", height=80):
    bs.Button("A"); bs.Button("B")

# center in cell at natural size
with bs.Grid(columns=[1, 1], gap=8, sticky_items="", height=80):
    bs.Button("A"); bs.Button("B")

# fill the entire cell
with bs.Grid(columns=[1, 1], gap=8, sticky_items="nsew", height=80):
    bs.Button("A"); bs.Button("B")
Grid sticky — light theme Grid sticky — dark theme

Auto-flow#

auto_flow= controls the direction children are placed when auto-flowing across the grid. Default is 'row' (left-to-right, then next row). Use 'column' to fill down first, then move to the next column. The '-dense' variants back-fill gaps left by larger spanning children.

bs.Grid(columns=[1, 1, 1], auto_flow="row")     # 1 2 3 / 4 5 6
bs.Grid(rows=[1, 1, 1], auto_flow="column")      # fills columns first

In context#

The most common Grid pattern is a two-column form layout with an 'auto' label column and a 1-weight field column:

with bs.Grid(columns=["auto", 1], gap=8, sticky_items="ew", fill="x"):
    bs.Label("First name")
    bs.TextField()
    bs.Label("Last name")
    bs.TextField()
    bs.Label("Email")
    bs.TextField()
    bs.Label("Role")
    bs.TextField()
Grid form layout — light theme Grid form layout — dark theme

Background#

surface= sets the container background. It accepts a surface token ('content', 'card', 'chrome', 'overlay') or any accent token ('primary', 'success', etc.) with optional modifiers:

with bs.Grid(columns=2, surface="card", padding=12, gap=8):
    bs.Label("Sits on card surface")

with bs.Grid(columns=2, surface="primary[subtle]", padding=12, gap=8):
    bs.Label("Accent-tinted background")

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#

Card and GroupBox both support layout='grid' to arrange their children with the same column/row options.

API#

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

Grid

A container that arranges children in rows and columns.

Full Example#

 1
 2with bs.App(title="Grid Demo", size=(720, 820), padding=20, gap=16) as app:
 3
 4    # Column definitions
 5    bs.Label("Column definitions", font="heading-sm")
 6    with bs.Grid(columns=[1, 1, 1], gap=8, show_border=True, padding=8,
 7                 sticky_items="ew", fill="x"):
 8        for label in ("Equal", "weight", "columns"):
 9            bs.Button(label)
10
11    with bs.Grid(columns=["auto", 1, "auto"], gap=8, show_border=True, padding=8,
12                 sticky_items="ew", fill="x"):
13        bs.Button("auto")
14        bs.Button("weight=1  (fills remaining)")
15        bs.Button("auto")
16
17    with bs.Grid(columns=["120px", 1, "80px"], gap=8, show_border=True, padding=8,
18                 sticky_items="ew", fill="x"):
19        bs.Button("120px")
20        bs.Button("weight=1")
21        bs.Button("80px")
22
23    # Gap
24    bs.Label("Gap", font="heading-sm")
25    with bs.Grid(columns=[1, 1], gap=(32, 16), fill="x", sticky_items="new"):
26        with bs.VStack(gap=4):
27            bs.Label("gap=8 (uniform)", font="caption")
28            with bs.Grid(columns=[1, 1], gap=8, show_border=True, padding=8,
29                         sticky_items="ew", fill="x"):
30                for label in ("A", "B", "C", "D"):
31                    bs.Button(label)
32        with bs.VStack(gap=4):
33            bs.Label("gap=(32, 8) (col, row)", font="caption")
34            with bs.Grid(columns=[1, 1], gap=(32, 8), show_border=True, padding=8,
35                         sticky_items="ew", fill="x"):
36                for label in ("A", "B", "C", "D"):
37                    bs.Button(label)
38
39    # sticky_items
40    bs.Label("sticky_items", font="heading-sm")
41    with bs.Grid(columns=[1, 1, 1], gap=(16, 0), fill="x", sticky_items="new"):
42        with bs.VStack(gap=4):
43            bs.Label("sticky='ew'", font="caption")
44            with bs.Grid(columns=[1, 1], gap=8, show_border=True, padding=8,
45                         sticky_items="ew", height=80, fill="x"):
46                bs.Button("A")
47                bs.Button("B")
48        with bs.VStack(gap=4):
49            bs.Label("sticky='' (centered)", font="caption")
50            with bs.Grid(columns=[1, 1], gap=8, show_border=True, padding=8,
51                         sticky_items="", height=80, fill="x"):
52                bs.Button("A")
53                bs.Button("B")
54        with bs.VStack(gap=4):
55            bs.Label("sticky='nsew'", font="caption")
56            with bs.Grid(columns=[1, 1], gap=8, show_border=True, padding=8,
57                         sticky_items="nsew", height=80, fill="x"):
58                bs.Button("A")
59                bs.Button("B")
60
61    # In context — key/value form layout
62    bs.Label("In context", font="heading-sm")
63    with bs.Grid(columns=["auto", 1], gap=8, show_border=True, padding=12,
64                 sticky_items="ew", fill="x"):
65        bs.Label("First name")
66        bs.TextField()
67        bs.Label("Last name")
68        bs.TextField()
69        bs.Label("Email")
70        bs.TextField()
71        bs.Label("Role")
72        bs.TextField()
73
74app.run()