CSS Grid layouts in one string. See demo.
A compact DSL that compiles layout strings into CSS Grid. One React component, optional extensions, zero wrapper divs.
<Grid layout="hsCf hhh scc sff 8">
<Header />
<Sidebar />
<Content />
<Footer />
</Grid>That's a full page layout. No CSS files, no class names, no nesting.
npm install gridpackimport { Grid } from "gridpack";Everything fits in one prop. The string has a simple grammar:
[|] [legend] [rows...] [gap] [?flags] [| col-sizes [| row-sizes]]
| String | Result |
|---|---|
ab |
Two equal columns |
|ab |
Two equal rows (transpose) |
ab abb |
Two columns, 1:2 ratio |
hsCf hhh scc sff 8 |
Holy grail, 8px gap, content grows |
* 8 |
Auto h-stack with gap (needs children) |
| 12 |
Auto v-stack with 12px gap |
*7 ?wh |
7-column auto-flow grid |
a(e)B ab* 8 | .# |
Form: labels right-aligned, inputs grow, repeat rows |
sa ss Sa* 8 | 120# |
Pinned sidebar + repeating list |
abc | 100~# 100~# 100~# |
3 columns, each min 100px |
| Token | Meaning |
|---|---|
a-z |
Named area |
A-Z |
Grow area (tracks become 1fr) |
. |
Empty cell (in map) / auto (in sizes) |
# |
1fr (in sizes) |
| |
Transpose prefix / pipe separator |
~ |
minmax(a, b) — e.g. 200~# |
* |
Auto-legend / repeat row |
? |
Flags (?w width, ?h height, ?cC center) |
( ) |
Per-area alignment — a(cC) centers area a |
{ } |
Template variable — {sidebar} |
Lowercase = justify-content, uppercase = align-content:
- S start · E end · C center · B space-between · A space-around · G space-evenly
?w/?h— force full width / height
Append * to a row to repeat it based on children count:
<Grid layout="habf hh ab* ff 8 | .#">
<Header />
<Footer />
{fields.map(f => <><Label /><Input /></>)}
</Grid>Uppercase letters in repeat rows are pinned — they span all repetitions:
// Sidebar spans all rows, items repeat next to it
<Grid layout="sa ss Sa* 8 | 120#">
<Sidebar />
{items.map(i => <Card />)}
</Grid>| Prop | Type | Description |
|---|---|---|
layout |
string |
Layout string |
col |
boolean |
Shorthand for transpose (| prefix) |
gap |
number | string |
Override gap |
vars |
object |
Values for {placeholder} substitution |
onVarsChange |
function |
Callback when extensions mutate vars |
extensions |
array |
Extension objects |
xs sm md lg xl |
string |
Layout strings per container breakpoint |
breaks |
object |
Custom breakpoint thresholds |
// Horizontal stack — no props needed
<Grid>
<A /> <B /> <C />
</Grid>
// Vertical stack
<Grid col>
<Header /> <Content /> <Footer />
</Grid>
// Responsive
<Grid layout="|abc ?w 8" sm="ab aab ?w 8" lg="abc ?w 8">
<A /> <B /> <C />
</Grid>Behavioral plugins. Composable. Stack them in an array.
import { Grid, splitPane, scrollable, debug } from "gridpack";
let [v, setV] = useState({ w: 200 });
<Grid
layout="sC | {w}"
vars={v}
onVarsChange={setV}
extensions={[
splitPane({ var: "w", edge: "s:e", min: 80, max: 400 }),
scrollable({ area: ["s", "c"] }),
debug(),
]}
>
<Sidebar />
<Content />
</Grid>| Extension | Description |
|---|---|
debug({ color? }) |
Grid cell overlay |
splitPane({ var, edge, min?, max? }) |
Draggable resize handle |
collapsible({ var, area, expanded?, collapsed? }) |
Toggle area size on click |
accordion({ var, items, collapsed? }) |
Mutual exclusion — expand one, collapse others |
scrollable({ area, axis? }) |
Independent scrolling per area |
overlay({ area, over }) |
Layer one area over another's grid cells |
animate({ properties?, duration?, easing? }) |
CSS transitions on track changes |
tabs({ var, items, position? }) |
Tab bar with content switching |
multiColumn({ area, fill? }) |
CSS columns aligned to grid tracks |
fisheye({ axis?, intensity?, min? }) |
Tracks expand near cursor, compress away |
Extensions are plain objects with lifecycle hooks:
let myExtension = (opts) => ({
name: "myExtension",
render: ({ parsed, vars, setVar, containerRef }) => [], // inject elements
containerStyle: ({ parsed, vars }) => ({}), // modify container
areaStyle: (area, vars) => null, // modify area wrappers
transformVars: (vars) => vars, // derive vars from vars
transformAreas: (parsed) => parsed, // modify parsed result
});layout = ["|"] [legend] [map-rows] [gap] [?flags] ["|" cols ["|" rows]]
legend = "*" | "*"digit+ | area-def+
area-def = letter | LETTER | letter"("mods")" | LETTER"("mods")"
mods = ("s"|"e"|"c"|"S"|"E"|"C")+
map-row = (letter|LETTER|".")+ ["*"]
gap = number [number]
?flags = "?" ("w"|"h"|"s"|"e"|"c"|"b"|"a"|"g"|"S"|"E"|"C"|"B"|"A"|"G")+
size = "." | "#" | number | atom"~"atom | css-literal
Implicit rules:
legend-only → single-row map
empty + children → "*" (auto-legend)
"|" + empty → transposed "*"
*N + children → N columns, auto rows
repeated chars → 1fr (proportional)
row ending "*" → repeat (varargs)
UPPER in repeat → pinned (shared across repetitions)
"{var}" → replaced from vars prop
| Traditional CSS Grid | Gridpack |
|---|---|
.layout {
display: grid;
grid-template-areas:
"header header header"
"sidebar content content"
"sidebar footer footer";
grid-template-columns: 200px 1fr 1fr;
grid-template-rows: auto 1fr auto;
gap: 8px;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.content { grid-area: content; }
.footer { grid-area: footer; } |
<Grid layout="hscf hhh scc sff 8 | 200##">
<Header />
<Sidebar />
<Content />
<Footer />
</Grid> |
- Playground — interactive demo with 35+ presets
- Documentation — full reference
- npm
If gridpack saves you time, consider supporting development:
MIT