Skip to content

Layout

Layout components map onto Bootstrap 5's grid and stack helpers: Container, Row, Col, and Stack. Use them to place content on a twelve-column grid that responds at the usual breakpoints, or to stack children without dropping into raw utility classes.

Container centers and horizontally pads a section of the page. Row wraps columns so they share a horizontal line. Col is a single grid cell, and Stack arranges a bundle of children along one axis.

For the grid to work, keep to two rules: put Row and Col inside a Container (or use Stack for non-grid bundles), and make Col the immediate child of Row. Content goes inside the Col. The snippets below are the same sources the demo service runs.

Basic usage

A Container holds a Row of Col children. width is the extra-small width; md (and friends, when you add them) override from that breakpoint up. The example below is half-and-half from md and full width on smaller viewports.

layout_simple example

def demo() -> None:
    with bs.scope(), bs.container(), bs.row():
        with bs.col(width=12, md=6):
            ui.label("Primary column").classes("border rounded p-2")
        with bs.col(width=12, md=6):
            ui.label("Secondary column").classes("border rounded p-2")

Row with columns

By default columns share the available width equally. Give a column an explicit width to change that. The accepted width values are:

  • True (the default): the column expands to fill the available space.
  • "auto": the column takes the natural width of its content.
  • An integer 1–12: the column spans that many of the twelve grid columns. Use width=6 for half, width=4 for a third, and so on.

layout_width example

def demo() -> None:
    cell_class = "border border-primary rounded p-2"
    with bs.scope(), bs.container():
        with bs.row(), bs.col(width=6, class_name=cell_class):
            ui.label("A single, half-width column")
        with bs.row(), bs.col(width="auto", class_name=cell_class):
            ui.label("An automatically sized column")
        with bs.row():
            with bs.col(width=3, class_name=cell_class):
                ui.label("One of three columns")
            with bs.col(class_name=cell_class):
                ui.label("One of three columns")
            with bs.col(width=3, class_name=cell_class):
                ui.label("One of three columns")

Specify order and offset

The width argument also accepts a dictionary with size, order, and offset keys.

  • size takes the same values as the plain width argument.
  • order reorders columns. It accepts integers or the strings "first" and "last". Columns sort numerically, with "first" and "last" at the extremes. Equal orders keep their source order.
  • offset increases the column's left margin by that many grid columns.

layout_order_offset example

def demo() -> None:
    cell_class = "border border-primary rounded p-2"
    with bs.scope(), bs.container():
        with bs.row(), bs.col(width={"size": 6, "offset": 3}, class_name=cell_class):
            ui.label("A single, half-width column")
        with bs.row():
            with bs.col(width={"size": 3, "order": "last", "offset": 1}, class_name=cell_class):
                ui.label("The last of three columns")
            with bs.col(width={"size": 3, "order": 1, "offset": 2}, class_name=cell_class):
                ui.label("The first of three columns")
            with bs.col(width={"size": 3, "order": 5}, class_name=cell_class):
                ui.label("The second of three columns")

Specify width for different screen sizes

Bootstrap's grid has six responsive tiers. Use the xs, sm, md, lg, xl, and xxl keyword arguments to set the size, order, and offset of a column for a screen size and up. Each takes the same values as width. width is shorthand for xs; if both are set, xs wins.

layout_breakpoints example

def demo() -> None:
    cell_class = "border border-primary rounded p-2"
    with bs.scope(), bs.container():
        with bs.row():
            with bs.col(md=4, class_name=cell_class):
                ui.label("One of three columns")
            with bs.col(md=4, class_name=cell_class):
                ui.label("One of three columns")
            with bs.col(md=4, class_name=cell_class):
                ui.label("One of three columns")
        with bs.row():
            with bs.col(width=6, lg=3, class_name=cell_class):
                ui.label("One of four columns")
            with bs.col(width=6, lg=3, class_name=cell_class):
                ui.label("One of four columns")
            with bs.col(width=6, lg=3, class_name=cell_class):
                ui.label("One of four columns")
            with bs.col(width=6, lg=3, class_name=cell_class):
                ui.label("One of four columns")

Row without 'gutters'

Rows add horizontal spacing between columns by default. Remove it with g=0, or adjust a single axis with gx/gy. All three take values 0–5.

layout_no_gutters example

def demo() -> None:
    cell_class = "border border-primary rounded p-2"
    with bs.scope(), bs.container(), bs.row(g=0):
        with bs.col(class_name=cell_class):
            ui.label("One of three columns")
        with bs.col(class_name=cell_class):
            ui.label("One of three columns")
        with bs.col(class_name=cell_class):
            ui.label("One of three columns")

Vertical alignment

Control vertical alignment with the align keyword on either Col or its parent Row. A value on the Col overrules the row. The options are "start", "center", and "end".

layout_vertical example

def demo() -> None:
    cell_class = "border border-primary rounded p-2"
    with bs.scope(), bs.container():
        for align in ("start", "center", "end"):
            with bs.row(align=align):
                with bs.col(class_name=cell_class):
                    ui.label("One of three columns")
                with bs.col(class_name=cell_class):
                    ui.label("One of three columns")
                with bs.col(class_name=cell_class):
                    ui.label("One of three columns")
        with bs.row():
            with bs.col(align="start", class_name=cell_class):
                ui.label("One of three columns")
            with bs.col(align="center", class_name=cell_class):
                ui.label("One of three columns")
            with bs.col(align="end", class_name=cell_class):
                ui.label("One of three columns")

Horizontal alignment

Control horizontal alignment with the justify keyword on Row. The options are "start", "center", "end", "between", "around", and "evenly".

layout_horizontal example

def demo() -> None:
    cell_class = "border border-primary rounded p-2"
    with bs.scope(), bs.container():
        for justify in ("start", "center", "end", "between", "around"):
            with bs.row(justify=justify):
                with bs.col(width=4, class_name=cell_class):
                    ui.label("One of two columns")
                with bs.col(width=4, class_name=cell_class):
                    ui.label("One of two columns")

Using only the grid components

To use the grid without Bootstrap's typography and component CSS, load the grid-only theme once at application startup instead of the full stylesheet:

from nicegui_bootstrap_components import themes
from nicegui_bootstrap_components.assets import StyleMode, setup

setup(mode=StyleMode.UNSCOPED, theme=themes.GRID)

That registers bootstrap-grid.css only. Run examples/components/layout/grid_only.py as a script to see it: the file's __main__ block performs this setup, then builds the page. To serve the file yourself instead, download bootstrap-grid.css and add a link to it from your page's assets directory.

The snippet and screenshot below are the gallery preview of that page body. The examples service calls setup(mode=StyleMode.MIXED) once for the whole process and cannot switch stylesheets per route, so this preview is not a grid-only theme. The bordered cells are still the real bs.container / bs.row / bs.col example.

layout_grid_only example

def demo() -> None:
    # Body only — do not call setup here (process-wide, already applied).
    # Standalone grid-only CSS is loaded in ``__main__``; the gallery stays MIXED.
    # Grid-only CSS has no border/padding utilities, and package style props
    # require dicts, so apply a CSS string via NiceGUI .style() on a nested element.
    cell_style = (
        "border: 1px solid #0d6efd; border-radius: 0.375rem; "
        "padding: 0.5rem; width: 100%; box-sizing: border-box;"
    )
    with bs.scope(), bs.container(), bs.row():
        with bs.col(width=6), ui.element().style(cell_style):
            ui.label("half-width")
        with bs.col(width=6), ui.element().style(cell_style):
            ui.label("half-width")

Stacking objects

Stack arranges objects along a vertical (default) or horizontal axis. Set gap between 0 and 5 to put a consistent gap between the items.

layout_simple_stack example

def demo() -> None:
    with bs.scope():
        with bs.stack():
            ui.label("This stack has no gaps").classes("border rounded p-2")
            ui.label("Next item").classes("border rounded p-2")
            ui.label("Last item").classes("border rounded p-2")
        ui.separator()
        with bs.stack(gap=3):
            ui.label("This stack has gaps").classes("border rounded p-2")
            ui.label("Next item").classes("border rounded p-2")
            ui.label("Last item").classes("border rounded p-2")

Use direction="horizontal" for horizontal layouts.

layout_horizontal_stack example

def demo() -> None:
    with bs.scope():
        with bs.stack(direction="horizontal"):
            ui.label("Horizontal").classes("border rounded p-2")
            ui.label("Stack").classes("border rounded p-2")
            ui.label("Without").classes("border rounded p-2")
            ui.label("Gaps").classes("border rounded p-2")
        ui.separator()
        with bs.stack(direction="horizontal", gap=3):
            ui.label("Horizontal").classes("border rounded p-2")
            ui.label("Stack").classes("border rounded p-2")
            ui.label("With").classes("border rounded p-2")
            ui.label("Gaps").classes("border rounded p-2")

Combine stacks with Bootstrap's spacing utilities for finer control. Here the middle item is pushed away from its neighbours with ms-auto and mx-auto:

layout_stack_spacers example

def demo() -> None:
    with bs.scope():
        with bs.stack(direction="horizontal", gap=3):
            ui.label("Start").classes("border rounded p-2")
            ui.label("Middle (ms-auto)").classes("ms-auto border rounded p-2")
            ui.label("End").classes("border rounded p-2")
        ui.separator()
        with bs.stack(direction="horizontal", gap=3):
            ui.label("Start").classes("border rounded p-2")
            ui.label("Middle (mx-auto)").classes("mx-auto border rounded p-2")
            ui.label("End").classes("border rounded p-2")

Notes

These helpers are structural. They have no n_clicks and no value, and cannot take user input. Put interactive callbacks on the controls you place inside the cells.

Argument reference

Property Type Default DBC 2.0.4 name Support (native) Support (compat)
children a list of or a singular dash component, string or number None children supported supported
id string None id supported supported
style unknown None style supported supported
class_name string None className supported supported
align a value equal to: 'start', 'center', 'end', 'stretch', 'baseline' None align supported supported
justify a value equal to: 'start', 'center', 'end', 'around', 'between', 'evenly' None justify supported supported
key string None key unsupported unsupported
class_name string None className supported supported