Still a WIP, but core functionality is there - Made mainly for my spectrum analyzer spectro
- I wanted a simple immediate mode flex layout library with as little internal state as possible.
- I wanted to build more advanced UIs faster that still looked good and didn't require so much manual control positioning.
- Favor Intuitiveness and Simplicty over Advanced Features
- Immediate Mode
- Intelligent, Declarative Layouts
- Simple Rows and Columns (thats it, maybe grids laters)
- Easy Stack-based nested layouts
- Stupid simple API (like 3 core functions)
- Resizeable and Auto-adjusting
- Mix flexbox-like and fixed sizes
- Vertical Alignment
- Tiny (core features less than 600 lines of code)
- Stack-based and fast
- Pure Header Only C for Native Raylib Integration
- Grid Layout (maybe)
- More alignments
- Profiling & Timings Output
Just a few lines of code gets you a nice layout. Inside your raylib drawing loop:
See [examples/sidebar/sidebar.odin] for full code including boilerplate
BeginDrawing()
ClearBackground(bg_color)
screen_w := f32(GetScreenWidth())
screen_h := f32(GetScreenHeight())
full_rect := rl.Rectangle{0, 0, screen_w, screen_h}
// Configure Raylay defaults
RLSetDefaultGap(10) // 10px gap between items
RLSetDefaultPadAll(0)
// Toplevel Column: 50px, stretch, 50px
RLBeginColumn(full_rect, plan = {50, -1, 50}, pad = PadAll(10))
// Header
RLBeginRow(RLNext(-1, -1)) // Use the plan size from the parent
DebugButtonEx(RLNext(100, -1)) // Logo Area
DebugButtonEx(RLNext(-1, -1)) // Rest of the headers
RLEnd()
// Content
RLBeginRow(RLNext(), plan = {1, 3, 1}) // Flex 1, 3, 1
DebugButtonEx(RLNext()) // Left Sidebar (deafults to -1, -1)
DebugButtonEx(RLNext()) // Center Content
DebugButtonEx(RLNext()) // Right Sidebar
RLEnd()
// Footer
RLBeginRow(RLNext())
DebugButtonEx(RLNext())
RLEnd()
RLEnd() // Toplevel Column
EndDrawing()Result:
This section documents the small set of core functions in Raylay. The library is intentionally tiny — you build layouts by creating containers (rows/columns), requesting child rectangles, then ending the container.
Core functions
-
RLBeginRow(bounds, pad = RL_DEFAULT_PAD, gap = RL_DEFAULT_GAP, plan = nil)- Begin a horizontal container (left → right).
- bounds: rl.Rectangle - outer bounds for the container.
- pad: RLPad - padding inside the container (defaults to RL_DEFAULT_PAD).
- gap: f32 - space between children (defaults to RL_DEFAULT_GAP).
- plan: []f32 (optional) - layout plan for children (see "Plan values" below).
- Use
RLBeginRowto push a row onto the internal stack; pair withRLEnd().
-
RLBeginColumn(bounds, pad = RL_DEFAULT_PAD, gap = RL_DEFAULT_GAP, plan = nil)- Begin a vertical container (top → bottom).
- Same parameters/semantics as
RLBeginRow, but lays out vertically.
-
RLNext(main = -1, cross = -1, pl = 0, pr = 0, pt = 0, pb = 0, valign = RL_DEFAULT_VALIGN) -> rl.Rectangle- Return the next child rectangle from the current container.
- main: f32
-
= 0: fixed size along the main axis (width for rows, height for columns).
- -1: in planned containers, use the next plan entry; in ad-hoc containers, fill the remaining main-axis space.
-
- cross: f32
-
= 0: fixed size along the cross axis (height for rows, width for columns).
- < 0: fill the entire cross axis available space.
-
- pl, pr, pt, pb: f32 - apply inline padding to the returned rectangle (left/right/top/bottom).
- valign: RLVAlign - vertical alignment for row containers (TOP, CENTER, BOTTOM, NONE). For column containers horizontal alignment is not yet supported.
- Returns: rl.Rectangle positioned and sized according to the current container flow.
-
RLNextPanel(main, cross, pad_top = 35.0, pad_other = 10.0) -> (rl.Rectangle, RLPad)- Convenience helper that calls
RLNext(main, cross)and returns a panel-styleRLPadfor title + margins. - Useful when placing framed panels with a header area.
- Convenience helper that calls
-
RLEnd()- Pop the current container from the internal stack. Call once per
RLBeginRow/RLBeginColumn.
- Pop the current container from the internal stack. Call once per
Defaults and setters
RLSetDefaultGap(g: f32)— set the default gap between children.RLSetDefaultPadAll(p: f32)— set a uniform default padding (applies to RL_DEFAULT_PAD).RLSetDefaultVAlign(align: RLVAlign)— set default vertical alignment forRLNextcalls.
Plan values (used by RLBeginRow/Column plan parameter)
v >= 20:fixed pixels. The child gets exactly v pixels on the main axis.0 < v < 20:flex weight. The value is treated as a weight; remaining flex space is divided proportionally.v == -1: "fill" — treated as a flex weight of 1 (fills remaining space). Multiple -1 entries share space.Values < -1are ignored (treated as zero).
Notes on planned vs ad-hoc mode
- Planned mode: supply a plan array when beginning a container. Each
RLNextwill use the next plan entry unless you explicitly pass a non-negative main value. - Ad-hoc mode: no plan provided. Each
RLNext(-1)will fill the remaining main-axis space; or pass a concrete main size to request a fixed size.
Quick tips
- Use small plan numbers (1,2,3...) to express flex ratios.
- Use values >= 20 when you need exact pixel sizes.

