Responsive layouts
Create responsive dashboards that adapt to different screen sizes.
ResponsiveFlexiBoard wraps a board and picks a layout for the current viewport width. Each breakpoint keeps its own widget arrangement. The example uses one board whose configuration reads the current breakpoint. Its utility classes use the docs example styling:
Breakpoint: default, 2 columns
<script lang="ts">
import { ResponsiveFlexiBoard, FlexiBoard, FlexiTarget, FlexiWidget } from '@flexiboards/svelte';
const tile = 'flex items-center justify-center rounded-lg bg-primary text-primary-foreground';
</script>
<ResponsiveFlexiBoard config={{ breakpoints: { lg: 1024 } }}>
{#snippet children({ currentBreakpoint })}
{@const columns = currentBreakpoint === 'lg' ? 3 : 2}
<FlexiBoard class="w-72 rounded-xl border p-6 lg:w-96">
<p class="text-muted-foreground mb-3 text-sm">
Breakpoint: {currentBreakpoint}, {columns} columns
</p>
<FlexiTarget
key="main"
class="gap-3"
config={{
rowSizing: '4rem',
layout: { type: 'free', minRows: 2, minColumns: columns, maxRows: 2, maxColumns: columns }
}}
>
<FlexiWidget x={0} y={0} class={tile}>A</FlexiWidget>
<FlexiWidget x={1} y={1} class={tile}>B</FlexiWidget>
</FlexiTarget>
</FlexiBoard>
{/snippet}
</ResponsiveFlexiBoard>Resize the browser across 1024px: the grid switches between three and two columns. Move a widget at one width, resize, and move it at the other, and each arrangement is remembered separately.
Breakpoints are independent
Shared board, breakpoint parameter
The example above uses a children fallback that receives the current breakpoint. This works well when you want the same board structure with different column counts or sizing:
<script lang="ts">
import { ResponsiveFlexiBoard, FlexiBoard, FlexiTarget } from '@flexiboards/svelte';
</script>
<ResponsiveFlexiBoard config={{ breakpoints: { lg: 1024, md: 768 } }}>
{#snippet children({ currentBreakpoint })}
<FlexiBoard>
<FlexiTarget
key="main"
config={{
layout: {
type: 'free',
minColumns: currentBreakpoint === 'lg' ? 4 : currentBreakpoint === 'md' ? 3 : 2,
maxColumns: currentBreakpoint === 'lg' ? 4 : currentBreakpoint === 'md' ? 3 : 2
}
}}
/>
</FlexiBoard>
{/snippet}
</ResponsiveFlexiBoard> Independent boards per breakpoint
For more control, give each breakpoint its own board content, so each one can use a different board structure. boardConfig below is a board configuration with a registry for the chart and stats types:
<script lang="ts">
import { ResponsiveFlexiBoard, FlexiBoard, FlexiTarget } from '@flexiboards/svelte';
import { boardConfig } from './board-config';
</script>
<ResponsiveFlexiBoard
config={{
breakpoints: { lg: 1024 },
loadLayouts: () => ({
lg: {
main: [
{ type: 'chart', x: 0, y: 0, width: 2, height: 2 },
{ type: 'stats', x: 2, y: 0, width: 1, height: 1 }
]
},
default: {
main: [
{ type: 'chart', x: 0, y: 0, width: 2, height: 2 },
{ type: 'stats', x: 0, y: 2, width: 2, height: 1 }
]
}
})
}}
>
{#snippet lg()}
<FlexiBoard config={boardConfig}>
<FlexiTarget key="main" config={{ layout: { type: 'free', minColumns: 3, maxColumns: 3 } }} />
</FlexiBoard>
{/snippet}
{#snippet children({ currentBreakpoint })}
<FlexiBoard config={boardConfig}>
<FlexiTarget key="main" config={{ layout: { type: 'free', minColumns: 2, maxColumns: 2 } }} />
</FlexiBoard>
{/snippet}
</ResponsiveFlexiBoard> Supported breakpoints
You can define breakpoints for these keys:
| Breakpoint | Description |
|---|---|
lg | Large screens |
md | Medium screens |
sm | Small screens |
xs | Extra-small screens |
A default breakpoint (which uses children) always implicitly exists, and is used if no breakpoint is matched.
Breakpoints are minimum viewport widths, evaluated largest-first. The first match wins. Add the breakpoints property below to your responsive configuration:
const config = {
breakpoints: {
lg: 1200, // viewport >= 1200px uses lg
md: 900, // viewport >= 900px uses md
sm: 600 // viewport >= 600px uses sm
// Below 600px, render children for the default breakpoint.
}
}; You don’t need to define all breakpoints. If only lg and children are defined, lg is used for large screens and children for everything else.
Import and export
Use the responsive controller’s importLayout() and exportLayout() to read or replace the collection of breakpoint layouts. These excerpts extend your existing responsive board. Define responsiveConfig with its breakpoints and loader, and retain the board content in the marked space. Wire save to your application’s save button:
<script lang="ts">
import { ResponsiveFlexiBoard, type ResponsiveFlexiBoardController } from '@flexiboards/svelte';
let responsiveBoard = $state<ResponsiveFlexiBoardController>();
function save() {
if (!responsiveBoard) return;
const layouts = responsiveBoard.exportLayout();
localStorage.setItem('layouts', JSON.stringify(layouts));
}
</script>
<ResponsiveFlexiBoard bind:controller={responsiveBoard} config={responsiveConfig}>
<!-- ... -->
</ResponsiveFlexiBoard> The responsive controller manages layouts for all breakpoints together.
When using responsive dashboards, use the responsive methods
Auto-persistence
For automatic saving, use loadLayouts and onLayoutsChange. These configuration excerpts keep your existing board content and registry. Stored data must contain layouts whose types exist in that registry:
<ResponsiveFlexiBoard
config={{
breakpoints: { lg: 1024 },
loadLayouts: () => {
const saved = localStorage.getItem('layouts');
return saved ? JSON.parse(saved) : undefined;
},
onLayoutsChange: (layouts) => {
localStorage.setItem('layouts', JSON.stringify(layouts));
}
}}
>
<!-- ... -->
</ResponsiveFlexiBoard> As with importing and exporting layouts, prefer these methods over the individual FlexiBoard’s methods on a responsive board.
Lazy initialisation
Server-side rendering
The server cannot know the viewport, so it guesses a breakpoint. Set ssrBreakpoint to the one most visitors land on, and see Server-Side Rendering for handling the mismatch.