FluidBox is built around one idea: layouts should bend to content, not the other way around.
Most layout libraries give you a rigid grid. FluidBox gives you proportions. A slice is not 300 pixels wide — it is twice as wide as its neighbour. When content changes, the layout breathes.
Principles:
- Fluid over fixed — flex proportions, not pixel widths. A
x={2}slice is always twice the size of ax={1}slice, regardless of screen size. - Two attributes, infinite layouts —
xfor horizontal,yfor vertical. That's the entire API for describing a layout tree. - Progressive control — works with zero options. Drag handles, persistence, and min-sizes are opt-in, not required.
- No dependencies — pure Svelte 5 + vanilla DOM. No external layout libraries, no CSS frameworks.
- Drag to resize — users own their layout. Resize handles appear on hover between any two adjacent slices.
- Persistent — pass a
layoutKeyand the user's resize preferences survive page reloads automatically.
FluidBox is currently a local component library. Copy the components into your Svelte 5 project:
cp src/Components/FluidBox.svelte your-project/src/Components/
cp src/Components/FluidSlice.svelte your-project/src/Components/
cp src/stores/layoutStore.js your-project/src/stores/<script>
import FluidBox from './Components/FluidBox.svelte';
import FluidSlice from './Components/FluidSlice.svelte';
</script>
<FluidBox>
<FluidSlice x={1} background="#e3f2fd" />
<FluidSlice x={2} background="#bbdefb" />
<FluidSlice y={1} background="#90caf9" />
</FluidBox>The layout produced by the above:
┌──────────────────────────────────────┐
│ x=1 │ x=2 │
│ │ │
├────────┴────────────────────────────┤
│ y=1 │
└──────────────────────────────────────┘
Layout algorithm: each time a child has a different axis than the previous one, everything built so far collapses into a single unit and the new child takes its proportional flex space alongside it — in the direction the new child specifies. This is fully recursive and unbounded: you can alternate axes as many times as you like and each axis change produces one more level of nesting.
| Prop | Type | Default | Description |
|---|---|---|---|
animated |
boolean |
true |
Enable CSS transitions on layout changes |
resizable |
boolean |
true |
Show drag handles between adjacent slices |
layoutKey |
string|null |
null |
localStorage key for auto-saving resize state |
initialLayout |
object |
{} |
Initial flex values map, e.g. { "0": 1.5, "1": 0.5 } |
dragSensitivity |
number |
1 |
Drag multiplier — 1 = 1:1 pixels, 2 = twice as fast |
Exposed method (via bind:this):
<FluidBox bind:this={box} />
<button onclick={() => console.log(box.getLayout())}>Export</button>getLayout() returns a plain object mapping slice keys to their current flex values.
| Prop | Type | Default | Description |
|---|---|---|---|
x |
number|null |
null |
Horizontal flex weight |
y |
number|null |
null |
Vertical flex weight |
animated |
boolean |
true |
Transition on flex changes |
background |
string |
"yellow" |
CSS background-color |
minSize |
number|null |
null |
Minimum pixel size during drag |
slicekey |
string|null |
null |
Stable key for layout persistence (recommended when using layoutKey) |
Only one of
xoryshould be provided per slice. The value is the flex weight relative to sibling slices.
<FluidBox layoutKey="my-dashboard">
<FluidSlice x={1} slicekey="sidebar" minSize={120} />
<FluidSlice x={3} slicekey="main" />
<FluidSlice y={1} slicekey="panel" minSize={80} />
</FluidBox>The user's resize preferences are saved to localStorage under "my-dashboard" automatically. On the next page load, the layout is restored. Use slicekey props to keep layout stable when slices are added or removed.
FluidBox supports recursive nesting — place a FluidBox inside a FluidSlice:
<FluidBox>
<FluidSlice x={1} background="#e3f2fd" />
<FluidSlice x={2}>
<FluidBox>
<FluidSlice y={1} background="#bbdefb" />
<FluidSlice y={1} background="#90caf9" />
</FluidBox>
</FluidSlice>
</FluidBox>┌──────────────────────────────┐
│ │ top panel │
│ ├─────────────────────│
│ x=1 │ bottom panel │
└──────────────────────────────┘
| Library | Approach | Bundle contribution¹ | Drag resize | Persistence | Nesting | Framework |
|---|---|---|---|---|---|---|
| golden-layout | JSON config, tab groups | ~180 KB | yes | yes | yes | Vanilla |
| react-mosaic | React, binary tree model | ~90 KB | yes | no | yes | React |
| FluidBox | Declarative, 2 attributes | ~5 KB | yes | yes | yes | Svelte 5 |
¹ Bundle contribution = the code each library adds to your app bundle (minified, no runtime/framework overhead).
FluidBox component source files total ~17.8 KB unminified (FluidBox.svelte16.2 KB +FluidSlice.svelte0.8 KB +FluidDivider.svelte0.1 KB +layoutStore.js0.6 KB). After Svelte compilation and minification the contribution to a host app bundle is ~5–6 KB (~2–3 KB gzipped). The Svelte runtime is excluded since it is a peer dependency already present in the host app.
golden-layout and react-mosaic figures are their published minified bundles including their own runtime code.
npm install
npm run dev # start dev server
npm run build # production build
npm run preview # preview production buildMIT — by Massimo Moffa