ui.layout_columns(*args, col_widths=None, row_heights=None, fill=True, fillable=True, gap=None, class_=None, height=None, min_height=None, max_height=None, **kwargs)

Create responsive, column-based grid layouts, based on a 12-column grid.


*args: TagChild | TagAttrs = ()

Child elements or attributes to be added to the layout.

col_widths: BreakpointsUser[int] = None

The widths of the columns, possibly at different breakpoints. Can be one of the following:

  • None (the default): Automatically determines a sensible number of columns based on the number of children given to the layout.
  • A list or tuple of integers between 1 and 12, where each element represents the number of columns for the relevant UI element. Column widths are recycled to extend the values in col_widths to match the actual number of items in the layout, and children are wrapped onto the next row when a row exceeds 12 column units. For example, col_widths=(4, 8, 12) allocates 4 columns to the first element, 8 columns to the second element, and 12 columns to the third element (which wraps to the next row). Negative values are also allowed, and are treated as empty columns. For example, col_widths=(-2, 8, -2) would allocate 8 columns to an element (with 2 empty columns on either side).
  • A dictionary of column widths at different breakpoints. The keys should be one of "xs", "sm", "md", "lg", "xl", or "xxl", and the values are either of the above. For example, col_widths={"sm": (3, 3, 6), "lg": (4)}.
row_heights: BreakpointsUser[CssUnit] = None

The heights of the rows, possibly at different breakpoints. Can be one of the following:

  • A numeric vector, where each value represents the fractional unit (fr) height of the relevant row. If there are more rows than values provided, the pattern will be repeated. For example, row_heights=(1, 2) allows even rows to take up twice as much space as odd rows.
  • A list of numeric or CSS length units, where each value represents the height of the relevant row. If more rows are needed than values provided, the pattern will repeat. For example, row_heights=["auto", 1] allows the height of odd rows to be driven my it’s contents and even rows to be 1fr.
  • A single string containing CSS length units. In this case, the value is supplied directly to grid-auto-rows.
  • A dictionary of row heights at different breakpoints, where each key is a breakpoint name (one of "xs", "sm", "md", "lg", "xl", or "xxl") and where the values may be any of the above options.
fill: bool = True

Whether or not to allow the layout to grow/shrink to fit a fillable container with an opinionated height (e.g., page_fillable).

fillable: bool = True

Whether or not each element is wrapped in a fillable container.

gap: Optional[CssUnit] = None

Any valid CSS unit to use for the gap between columns.

class_: Optional[str] = None

CSS class(es) to apply to the containing element.

height: Optional[CssUnit] = None

A valid CSS unit (e.g., height="200px"). Use min_height and max_height in a filling layout to ensure that the layout container does not shrink below a min_height or grow beyond a max_height.

**kwargs: TagAttrValue = {}

Additional attributes to apply to the containing element.


Type Description
Tag An Tag element.

See Also



#| standalone: true
#| components: [editor, viewer]
#| layout: vertical
#| viewerHeight: 400

## file: app.py
from model_plots import *  # model plots and cards

from shiny import App, Inputs, Outputs, Session, render, ui

app_ui = ui.page_fluid(
    ui.panel_title(ui.h2("Model Dashboard")),
    ui.markdown("Using `ui.layout_columns()` for the layout."),
        col_widths={"sm": (5, 7, 12)},
        # row_heights=(2, 3),
        # height="700px",

def server(input: Inputs, output: Outputs, session: Session):
    def loss_over_time():
        return plot_loss_over_time()

    def accuracy_over_time():
        return plot_accuracy_over_time()

    def feature_importance():
        return plot_feature_importance()

app = App(app_ui, server)

## file: model_plots.py
import matplotlib.pyplot as plt
import numpy as np

from shiny import ui

def plot_loss_over_time():
    epochs = np.arange(1, 101)
    loss = 1000 / np.sqrt(epochs) + np.random.rand(100) * 25

    fig = plt.figure(figsize=(10, 6))
    plt.plot(epochs, loss)
    return fig

def plot_accuracy_over_time():
    epochs = np.arange(1, 101)
    accuracy = np.sqrt(epochs) / 12 + np.random.rand(100) * 0.15
    accuracy = [np.min([np.max(accuracy[:i]), 1]) for i in range(1, 101)]

    fig = plt.figure(figsize=(10, 6))
    plt.plot(epochs, accuracy)
    return fig

def plot_feature_importance():
    features = ["Product Category", "Price", "Brand", "Rating", "Number of Reviews"]
    importance = np.random.rand(5)

    fig = plt.figure(figsize=(10, 6))
    plt.barh(features, importance)
    return fig

card_loss = ui.card(
    ui.card_header("Loss Over Time"),

card_acc = ui.card(
    ui.card_header("Accuracy Over Time"),

card_feat = ui.card(
    ui.card_header("Feature Importance"),