Skip to content

Layout Grid

Use Grid for layouts where vertical and horizontal relationships are intricate, such as complex dashboards or application screens with headers and sidebars. Arrangements that are difficult to express with just nested Columns and Rows can be described cleanly with Grid.

Finished Image

In this guide, we will learn how to use Grid by building a common application layout step-by-step.

App layout grid example

Step 1: Simple Placement (3 Rows x 2 Columns)

First, create a grid that will be the skeleton of the layout. Here we define a 3-row x 2-column grid and place content in each cell. By specifying "33%" or "50%" in rows and columns arguments, we divide the entire screen equally.

# Define a 3-row x 2-column grid
import nuiitivet as nv
import nuiitivet.material as md

nv.Grid(
    rows=["33%", "33%", "33%"],
    columns=["50%", "50%"],
    children=[
        # [0, 0] Planned for Header
        nv.GridItem(_card("Header"), row=0, column=0),

        # [0, 1] Right Top (Will be expanded by Header later)
        nv.GridItem(_card("(Empty)"), row=0, column=1),

        # [1, 0] Planned for Sidebar
        nv.GridItem(_card("Sidebar"), row=1, column=0),

        # [1, 1] Main content
        nv.GridItem(_card("Main"), row=1, column=1),

        # [2, 0] Left Bottom (Will be expanded by Sidebar later)
        nv.GridItem(_card("(Empty)"), row=2, column=0),

        # [2, 1] Planned for Footer
        nv.GridItem(_card("Footer"), row=2, column=1),
    ],
    padding=12,
    row_gap=12, 
    column_gap=12,
)

# Helper for creating cards
def _card(label: str, width="100%", height="100%") -> md.Card:
    return md.Card(
        md.Text(label),
        padding=12,
        alignment="center",
        width=width,
        height=height,
    )

Step 1 Simple Grid

Cells marked (Empty) are places planned to be merged later. We will remove them shortly.

Step 2: Merging Cells

Next, merge cells to expand the header horizontally and the sidebar vertically.

By passing a list or tuple like (start, end) to column or row arguments, you can span placement across multiple cells.

nv.Grid(
    rows=["33%", "33%", "33%"],
    columns=["50%", "50%"],

    children=[
        # Header: Span columns 0 to 1 horizontally
        nv.GridItem(_card("Header"), row=0, column=(0, 1)),

        # Sidebar: Span rows 1 to 2 vertically
        nv.GridItem(_card("Sidebar"), row=(1, 2), column=0),

        # Main:
        nv.GridItem(_card("Main"), row=1, column=1),

        # Footer:
        nv.GridItem(_card("Footer"), row=2, column=1),
    ],

    padding=12,
    row_gap=12, 
    column_gap=12,
)

Step 2 Spanning

Although not visible, the (Empty) places have been absorbed by the header and sidebar respectively.

Step 3: Adjusting Row and Column Sizes

Decide the sizes of rows and columns. Sizes can be specified in the following three ways as explained in Layout Sizing.

  1. Fixed: Specify by number (e.g., 60).
  2. Auto: "auto". Fit to content size.
  3. Flex: Specify by percentage (e.g., "50%"). Distribute remaining space by specified ratio.

Here, we define each component as follows:

  • Header: Height 60px
  • Sidebar: Auto width ("auto")
  • Footer: Auto height ("auto")
  • Main: Use all remaining space ("100%")
nv.Grid(
    # Rows: [Header fixed 60px, Main rest, Footer auto]
    rows=[60, "100%", "auto"],

    # Columns: [Sidebar auto, Main rest]
    columns=["auto", "100%"],

    children=[
        # Header: Span columns 0 to 1 horizontally
        nv.GridItem(_card("Header"), row=0, column=(0, 1)),

        # Sidebar: Span rows 1 to 2 vertically
        nv.GridItem(_card("Sidebar"), row=(1, 2), column=0),

        # Main:
        nv.GridItem(_card("Main"), row=1, column=1),

        # Footer:
        nv.GridItem(_card("Footer"), row=2, column=1),
    ],

    padding=12,
    row_gap=12, 
    column_gap=12,
)

Step 3 Sizing Strategies

The appearance is still broken because we haven't adjusted the content size inside the cells. We will fix this in the next step.

Step 4: Content Size in Cells

Decide the size of the content placed inside the cells.

Here we set it as follows:

  • Header:
  • width: "100%"
  • height: "100%"
  • Sidebar:
  • width: None (Auto)
  • height: "100%"
  • Footer:
  • width: "100%"
  • height: None (Auto)
  • Main:
  • width: "100%"
  • height: "100%"
import nuiitivet as nv
import nuiitivet.material as md

nv.Grid(
    # Rows: [Header fixed 60px, Main rest, Footer auto]
    rows=[60, "100%", "auto"],

    # Columns: [Sidebar auto, Main rest]
    columns=["auto", "100%"],

    children=[
        # Header:
        #   Span columns 0 to 1 horizontally
        #   Specify width/height as 100%
        nv.GridItem(_card("Header"), row=0, column=[0, 1]), 

        # Sidebar:
        #   Span rows 1 to 2 vertically
        #   Specify width as None (Auto), height as 100%
        nv.GridItem(_card("Sidebar", width=None), row=[1, 2], column=0),

        # Main:
        #   Specify width/height as 100%
        nv.GridItem(_card("Main content"), row=1, column=1), 

        # Footer:
        #   Specify width as 100%, height as None (Auto)
        nv.GridItem(_card("Footer", height=None), row=2, column=1)
    ],

    row_gap=12,
    column_gap=12,
    padding=12,    
)

Step 4 Expansion

Placement by Named Areas (Named Areas)

In addition to index numbers, you can also use area names to specify placement. This is convenient when you want to visually define complex layouts.

By using Grid.named_areas() and GridItem.named_area(), you can clarify your intent.

nv.Grid.named_areas(
    rows=[60, "100%", "auto"],
    columns=["auto", "100%"],
    # Layout map definition
    areas=[
        ["header",  "header"],
        ["sidebar", "content"],
        ["sidebar", "footer"],
    ],
    children=[
        nv.GridItem.named_area(_card("Header"), "header"),
        nv.GridItem.named_area(_card("Sidebar", width=None), "sidebar"),
        nv.GridItem.named_area(_card("Main content"), "content"),
        nv.GridItem.named_area(_card("Footer", height=None), "footer"),
    ],
    row_gap=12,
    column_gap=12,
    padding=12,
)

Named areas grid example