Skip to content

Layout

The layout resource defines the visual structure of the lab UI through columns and rows. Layouts organize how tabs (terminal, service, editor, etc.) and instructions are displayed to users.

resource "layout" "name" {
column {
instructions {}
}
column {
tab "terminal" {
target = resource.terminal.main
}
}
}
resource "layout" "name" {
column {
width = "percentage"
tab "name" {
target = resource.type.name
title = "Display Title"
active = true
visible = true
closeable = false
movable = false
}
instructions {
title = "Instructions"
active = false
}
row {
height = "percentage"
# nested content
}
}
}

layout

FieldRequiredTypeDescription
columnblockColumn definitions (repeatable)

The layout resource uses a hierarchical structure:

  • Columns: Vertical divisions of the layout
  • Rows: Horizontal divisions within columns
  • Tabs: Content panels that reference other resources
  • Instructions: Special panel for lab instructions

layout → column

A column is a vertical panel within the layout. Columns do not have names or IDs - they are anonymous blocks.

FieldRequiredTypeDescription
widthstringWidth of the column as a percentage (e.g., “50%”, “33%”). Defaults to equal distribution.
tabblockTab blocks defining content panels
instructionsblockInstructions panel configuration
rowblockNested row blocks for further subdivision

layoutcolumn → row

A row is a horizontal panel within a column. Like columns, rows are anonymous blocks without names or IDs.

FieldRequiredTypeDescription
heightstringHeight of the row as a percentage. Defaults to equal distribution.
tabblockTab blocks defining content panels
instructionsblockInstructions panel configuration
columnblockNested column blocks for further subdivision

layout → tab

Tabs define the actual content panels within the layout. Each tab must have a name (used as a label) and references a resource.

FieldRequiredTypeDescription
namelabelThe identifier for the tab (becomes the label)
titlestringDisplay title for the tab. Defaults to capitalized name.
targetreference to terminal, service, editor, external_website, noteReference to the resource to display
activeboolWhether the tab is initially active. Defaults to false.
visibleboolWhether the tab is visible. Defaults to true.
closeableboolWhether users can close the tab. Defaults to false.
movableboolWhether users can move the tab. Defaults to false.
Resource Type Description Reference
terminal Interactive command-line interface Terminal
service Web applications and services Service
editor Code editing environment Editor
external_website External website integration External Website
note Reference materials and documentation Note

layout → instructions

The instructions block configures the special instructions panel.

FieldRequiredTypeDescription
titlestringTitle of the instructions tab. Defaults to “Instructions”.
activeboolWhether the tab is initially active. Defaults to false.
visibleboolWhether the tab is visible. Defaults to true.
closeableboolWhether users can close the tab. Defaults to false.
movableboolWhether users can move the tab. Defaults to false.
  • Tab names must be unique within a layout and follow identifier rules (alphanumeric and underscore only, cannot start with a number)
  • The name “instructions” is reserved and cannot be used for tab names
  • Tab targets must reference existing resources of supported types
  • Width and height values must be valid CSS percentages (e.g., “50%”, “33.33%”)
  • Percentages in a column/row should ideally sum to 100% for predictable layout behavior
resource "layout" "two_column" {
column {
width = "50%"
instructions {}
}
column {
width = "50%"
tab "terminal" {
target = resource.terminal.main
}
}
}
resource "layout" "complex" {
column {
width = "40%"
instructions {}
}
column {
width = "60%"
row {
height = "70%"
tab "terminal" {
target = resource.terminal.main
active = true
}
tab "editor" {
target = resource.editor.code
}
}
row {
height = "30%"
tab "service" {
target = resource.service.web
}
}
}
}
resource "layout" "three_column" {
column {
width = "25%"
instructions {}
}
column {
width = "50%"
tab "terminal" {
target = resource.terminal.main
active = true
}
tab "terminal2" {
target = resource.terminal.secondary
}
}
column {
width = "25%"
tab "notes" {
target = resource.note.hints
}
}
}
resource "layout" "nested" {
column {
width = "30%"
row {
height = "50%"
instructions {}
}
row {
height = "50%"
tab "notes" {
target = resource.note.hints
}
}
}
column {
width = "70%"
row {
height = "60%"
column {
width = "60%"
tab "terminal" {
target = resource.terminal.main
}
}
column {
width = "40%"
tab "editor" {
target = resource.editor.config
}
}
}
row {
height = "40%"
tab "service" {
target = resource.service.app
}
}
}
}

Breaking Change: The layout format has changed significantly from earlier versions:

  • Old format: Layout resources defined columns with IDs, tabs were configured separately in the lab resource referencing these column IDs
  • New format: Layout resources define columns with tabs inline, no separate tab configuration needed

If migrating from the old format:

layouts.hcl
## OLD (no longer valid)
resource "layout" "two_column" {
column "terminal" {}
column "instructions" {
width = 33
}
}
# main.hcl
resource "lab" "example" {
layout "two_column" {
reference = resource.layout.two_column
tab "terminal" {
panel = "terminal" # References column ID
target = resource.terminal.shell
}
instructions {
panel = "instructions" # References column ID
}
}
}
## NEW (current format)
resource "layout" "two_column" {
column {
tab "terminal" {
target = resource.terminal.shell
}
}
column {
width = "33%"
tab "instructions" {
type = "instructions"
}
}
}
resource "lab" "example" {
layout = resource.layout.two_column
}
  1. Start Simple: Begin with a two-column layout (instructions + terminal)
  2. Responsive Design: Consider how your layout will appear on different screen sizes
  3. Tab Organization: Group related tabs in the same column/row
  4. Active Tabs: Set one tab per column/row as active for better UX
  5. Avoid Deep Nesting: Limit nesting to 2-3 levels for maintainability