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#

A grid is the two-dimensional layout: children auto-flow into cells left-to-right and top-to-bottom, or you pin one to an exact row= / column= (spanning with rowspan= / columnspan=). Reach for it to align content into a table; a Row or Column for a single axis.

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-weight columns (columns=3 is the same shorthand)
with bs.Grid(columns=[1, 1, 1], gap=8, horizontal="stretch"):
    for label in ("Equal", "Weight", "Columns"):
        bs.Button(label)

# Label column sizes to content; field column takes the rest
with bs.Grid(columns=["auto", 1], gap=8, horizontal="stretch"):
    bs.Button("auto")
    bs.Button("weight=1 (fills remaining)")

# Fixed sidebar, flexible content, fixed panel
with bs.Grid(columns=["120px", 1, "80px"], gap=8, horizontal="stretch"):
    bs.Button("120px")
    bs.Button("weight=1")
    bs.Button("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.

# 8 px between all cells
with bs.Grid(columns=[1, 1], gap=8):
    for label in ("A", "B", "C", "D"):
        bs.Button(label)

# 32 px between columns, 8 px between rows
with bs.Grid(columns=[1, 1], gap=(32, 8)):
    for label in ("A", "B", "C", "D"):
        bs.Button(label)
Grid gap — light theme Grid gap — dark theme

In-cell alignment#

horizontal_items= and vertical_items= control how children align within their cell. Both default to 'stretch' (the child fills the cell on that axis); set either to 'left' / 'center' / 'right' or 'top' / 'center' / 'bottom' to place the child at its natural size instead.

Individual children can override the defaults with their own horizontal= / vertical=.

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

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

# fill the entire cell (the default on both axes)
with bs.Grid(columns=[1, 1], gap=8, 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, horizontal="stretch"):
    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 — Column / Row parents use the layout kwargs below, grid-based parents use grid kwargs.

Column (vertical layout)

Used inside a Column, App, or any other container with a column layout. Children are arranged top-to-bottom, so horizontal aligns each child across the width and grow shares the vertical space. (vertical does not apply — the order of the children sets their top-to-bottom position.)

horizontal

Cross-axis placement of the widget: 'left', 'center', 'right', or 'stretch' to fill the available width.

grow

Claim and fill a share of the leftover vertical space (the layout direction). True or False.

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=.

Row (horizontal layout)

Used inside a Row or any other container with a row layout. Children are arranged left-to-right, so vertical aligns each child across the height and grow shares the horizontal space. (horizontal does not apply — the order of the children sets their left-to-right position.)

vertical

Cross-axis placement of the widget: 'top', 'center', 'bottom', or 'stretch' to fill the available height.

grow

Claim and fill a share of the leftover horizontal space (the layout direction). True or False.

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.

horizontal

Horizontal placement within the grid cell: 'left', 'center', 'right', or 'stretch' to fill the cell width.

vertical

Vertical placement within the grid cell: 'top', 'center', 'bottom', or 'stretch' to fill the cell height.

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", minsize=(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                 vertical_items="center", horizontal="stretch"):
 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                 vertical_items="center", horizontal="stretch"):
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                 vertical_items="center", horizontal="stretch"):
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), horizontal="stretch", vertical_items="top"):
26        with bs.Column(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                         vertical_items="center", horizontal="stretch"):
30                for label in ("A", "B", "C", "D"):
31                    bs.Button(label)
32        with bs.Column(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                         vertical_items="center", horizontal="stretch"):
36                for label in ("A", "B", "C", "D"):
37                    bs.Button(label)
38
39    # In-cell alignment
40    bs.Label("In-cell alignment", font="heading-sm")
41    with bs.Grid(columns=[1, 1, 1], gap=(16, 0), horizontal="stretch", vertical_items="top"):
42        with bs.Column(gap=4):
43            bs.Label("vertical_items='center'", font="caption")
44            with bs.Grid(columns=[1, 1], rows=[1], gap=8, show_border=True, padding=8,
45                         vertical_items="center", height=80, horizontal="stretch"):
46                bs.Button("A")
47                bs.Button("B")
48        with bs.Column(gap=4):
49            bs.Label("horizontal_items='center'", font="caption")
50            with bs.Grid(columns=[1, 1], rows=[1], gap=8, show_border=True, padding=8,
51                         horizontal_items="center", vertical_items="center", height=80, horizontal="stretch"):
52                bs.Button("A")
53                bs.Button("B")
54        with bs.Column(gap=4):
55            bs.Label("stretch (default)", font="caption")
56            with bs.Grid(columns=[1, 1], rows=[1], gap=8, show_border=True, padding=8,
57                         height=80, horizontal="stretch"):
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                 vertical_items="center", horizontal="stretch"):
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()