Flow grids
Learn how to use flow grids for Kanban and ordered layouts.
A flow grid keeps widgets in order and packs them densely, like a list. Set a target’s layout.type to 'flow' to get one:
<script lang="ts">
import { FlexiBoard, FlexiTarget, FlexiWidget } from '@flexiboards/svelte';
</script>
<FlexiBoard class="size-72 rounded-xl border p-8 lg:size-96">
<FlexiTarget
class={'h-full w-full gap-4 lg:gap-6'}
containerClass={'w-full h-full'}
config={{
rowSizing: 'minmax(0, 1fr)',
layout: {
type: 'flow',
rows: 4,
columns: 1,
placementStrategy: 'append',
flowAxis: 'row'
}
}}
>
<FlexiWidget class="bg-primary text-primary-foreground rounded-lg px-4 py-2">
{#snippet children({ widget, component, componentProps })}
I'm at ({widget.x}, {widget.y})
{/snippet}
</FlexiWidget>
<FlexiWidget class="bg-secondary text-secondary-foreground rounded-lg px-4 py-2">
{#snippet children({ widget, component, componentProps })}
And I'm at ({widget.x}, {widget.y})
{/snippet}
</FlexiWidget>
</FlexiTarget>
</FlexiBoard>Both widgets sit in a single column. Drag one onto the other and they swap; drop into an empty cell and the rest reflow to close the gap.
When to use a flow grid
Use a flow grid when order matters more than position: Kanban columns, sortable lists, and galleries. Widgets never have gaps between them, and a widget dropped into the grid takes an index rather than coordinates. For dashboards where widgets live at fixed coordinates, use a free-form grid.
Configuring the flow
Three properties shape a flow grid:
flowAxischooses whether widgets run along rows ('row') or columns ('column').placementStrategydecides where a widget lands when it is added without a position:'append'at the end,'prepend'at the start.rowsandcolumnsfix the grid’s size. Leave one open and the grid grows along the flow axis, capped bymaxFlowAxisif you set it.
Every property, with its type and default, is in the FlowTargetLayout reference.
Extension to 2D
When the cross dimension (columns for row flow, rows for column flow) is greater than 1, the flow wraps across it. Widgets fill the cross dimension as far as they can while keeping their order, so a widget that cannot fit in the current row leaves a gap and starts the next one.
Below, widget B has a width of 2, so it always takes a row of its own. Placed after A or C, it would not fit beside them.
<script lang="ts">
import { FlexiBoard, FlexiTarget, FlexiWidget } from '@flexiboards/svelte';
</script>
<FlexiBoard class="size-72 rounded-xl border p-8 lg:size-96">
<FlexiTarget
class={'h-full w-full gap-4 lg:gap-6'}
containerClass={'w-full h-full'}
config={{
rowSizing: 'minmax(0, 1fr)',
layout: {
type: 'flow',
rows: 4,
columns: 2,
placementStrategy: 'append',
flowAxis: 'row'
}
}}
>
<FlexiWidget class="bg-primary text-primary-foreground rounded-lg px-4 py-2">A</FlexiWidget>
<FlexiWidget class="bg-secondary text-secondary-foreground rounded-lg px-4 py-2" width={2}>
B
</FlexiWidget>
<FlexiWidget class="bg-primary text-primary-foreground rounded-lg px-4 py-2">C</FlexiWidget>
</FlexiTarget>
</FlexiBoard>Examples
The Notes example uses nested 1D flow grids, and Flow is a 2D flow grid.
Gotchas
- Flow-axis size is always 1. A widget’s
heightin row flow (orwidthin column flow) is ignored. WithrowSizingorcolumnSizingset toauto, cells still stretch to fit content of different sizes. - Drop targets are whole cells. Dropping onto a widget places the dragged widget after it when moving forwards and before it when moving backwards. Changing side on the same widget needs real pointer travel, so widgets reflowing under a still pointer never flicker.
- Insertion can be disabled. Set
disallowInsert: trueand every drop usesplacementStrategyinstead of the pointer position, which suits “add to end” inboxes.