Responsive Layouts

Learn how to create responsive dashboards that adapt to different screen sizes.

Introduction

The ResponsiveFlexiBoard component wraps your board and manages different layouts for different viewport sizes. Each breakpoint can have its own widget arrangement, and layouts are automatically persisted per breakpoint.

Breakpoints are independent
Each breakpoint renders its own separate Flexiboard. Moving, resizing, adding, or removing widgets only affects the currently active breakpoint - changes don't sync across breakpoints.

Shared Layout with Breakpoint Parameter

The simplest approach is to use a single children snippet that receives the current breakpoint:

<script lang="ts">
	import {
		ResponsiveFlexiBoard,
		FlexiBoard,
		FlexiTarget
	} from 'svelte-flexiboards';
</script>

<ResponsiveFlexiBoard config={{ breakpoints: { lg: 1024, md: 768 } }}>
	{#snippet children({ currentBreakpoint })}
		<FlexiBoard>
			<FlexiTarget
				key="main"
				config={{
					layout: {
						type: 'free',
						columns: currentBreakpoint === 'lg' ? 4 : currentBreakpoint === 'md' ? 3 : 2
					}
				}}
			/>
		</FlexiBoard>
	{/snippet}
</ResponsiveFlexiBoard>

This approach works well when you want the same board structure but with different column counts or sizing.

Independent Layouts Per Breakpoint

For more control, define separate snippets for each breakpoint. This lets you use entirely different board structures:

<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', columns: 3 } }}
			/>
		</FlexiBoard>
	{/snippet}

	{#snippet children({ currentBreakpoint })}
		<FlexiBoard config={boardConfig}>
			<FlexiTarget
				key="main"
				config={{ layout: { type: 'free', columns: 2 } }}
			/>
		</FlexiBoard>
	{/snippet}
</ResponsiveFlexiBoard>

Supported Breakpoints

You can define breakpoints for these keys:

SnippetDescription
lgLarge screens
mdMedium screens
smSmall screens
xsExtra-small screens

A default breakpoint (which uses the children snippet) always implicitly exists, and is used if no breakpoint is matched.

Breakpoints are defined as minimum viewport widths. They’re evaluated largest-first, and the first match wins:

breakpoints: {
	lg: 1200,  // viewport >= 1200px uses lg
	md: 900,   // viewport >= 900px uses md
	sm: 600    // viewport >= 600px uses sm
	// < 600px falls back to children (or '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 & Export

When using ResponsiveFlexiBoard, use the responsive controller’s importLayout() and exportLayout() methods instead of the inner FlexiBoard’s methods:

<script lang="ts">
	import { ResponsiveFlexiBoard, type ResponsiveFlexiBoardController } from 'svelte-flexiboards';

	let responsiveBoard: ResponsiveFlexiBoardController;

	function save() {
		const layouts = responsiveBoard.exportLayout();
		localStorage.setItem('layouts', JSON.stringify(layouts));
	}
</script>

<ResponsiveFlexiBoard bind:controller={responsiveBoard} config={...}>
	<!-- ... -->
</ResponsiveFlexiBoard>

The responsive controller manages layouts for all breakpoints together.

When using responsive dashboards, use the responsive methods
When a board is rendering in a responsive context, calling `importLayout()` or `exportLayout()` on the inner `FlexiBoard` will log a warning. Always use the `ResponsiveFlexiBoard` controller's methods instead.

Auto-Persistence

For automatic saving, use loadLayouts and onLayoutsChange:

<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 / exporting layouts, prefer the use of these methods over the individual FlexiBoard’s methods when using a responsive board.

Lazy initialisation
Layouts are created on-demand. If a user never resizes their viewport to trigger a breakpoint, no layout is stored for it. The `onLayoutsChange` callback only includes breakpoints that have been visited.