# Responsive layouts

> Create responsive dashboards that adapt to different screen sizes.

Source: https://www.flexiboards.dev/docs/guides/responsive-layouts

Framework: Svelte

`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](https://www.flexiboards.dev/docs/overview#example-styling):

Example: Responsive columns

```svelte
<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**
>
> 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 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:

```svelte
<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](https://www.flexiboards.dev/docs/guides/exporting-importing-boards#the-registry) for the `chart` and `stats` types:

```svelte
<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:

```typescript
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:

```svelte
<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**
>
> 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`. These configuration excerpts keep your existing board content and registry. Stored data must contain layouts whose types exist in that registry:

```svelte
<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**
>
> 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.

## 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](https://www.flexiboards.dev/docs/guides/server-side-rendering#responsive-boards) for handling the mismatch.
