Skip to Content
New — snapgrid now ships for Svelte 5: the same dnd-kit-native, headless-first grid — drag, resize, repack, and drag between grids. See it in Svelte →
VueDocumentationGuidesResponsive layouts

Responsive layouts

A responsive grid switches column count and layout as the container width crosses breakpoints. The headless engine is useResponsiveLayout: give it a map of per-breakpoint layouts and the measured width, and it resolves the active breakpoint’s cols and layout plus an onLayoutChange that writes back to the right breakpoint. Feed those into useGridContainer.

<!-- ResponsiveBoard.vue --> <script setup> import { ref } from "vue"; import { DragDropProvider, useContainerWidth, useResponsiveLayout } from "@snapgridjs/vue"; import Grid from "./Grid.vue"; const { width, setRef } = useContainerWidth(); const layouts = ref({ lg: [ { i: "a", x: 0, y: 0, w: 4, h: 2 }, { i: "b", x: 4, y: 0, w: 4, h: 2 }, { i: "c", x: 8, y: 0, w: 4, h: 2 }, ], }); // Resolve the active breakpoint's column count + layout from the width. Options are // passed as a getter so the result tracks the live width and layouts. const { cols, layout, onLayoutChange } = useResponsiveLayout(() => ({ width: width.value, layouts: layouts.value, onLayoutChange: (_active, all) => (layouts.value = all), onBreakpointChange: (bp, activeCols) => console.log("now at", bp, activeCols), })); </script> <template> <div :ref="setRef"> <DragDropProvider> <Grid :layout="layout" :width="width" :cols="cols" @layout-change="onLayoutChange" /> </DragDropProvider> </div> </template>
<!-- Grid.vue — the column count tracks the active breakpoint, so the grid reflows with it. --> <script setup> import { useGridContainer } from "@snapgridjs/vue"; import Tile from "./Tile.vue"; const props = defineProps({ layout: Array, width: Number, cols: Number }); const emit = defineEmits(["layoutChange"]); const { setRef, style, group } = useGridContainer(() => ({ layout: props.layout, width: props.width, gridConfig: { cols: props.cols }, onLayoutChange: (next) => emit("layoutChange", next), })); </script> <template> <div :ref="setRef" :style="style"> <Tile v-for="it in layout" :key="it.i" :id="it.i" :group="group" /> </div> </template>

Missing breakpoints are generated from the nearest provided one.

Responsive
drag the right edge — columns change at breakpoints

Breakpoints & columns

Defaults mirror react-grid-layout:

BreakpointMin width (px)Columns
lg120012
md99610
sm7686
xs4804
xxs02

Override either with the breakpoints and cols options (exported as DEFAULT_BREAKPOINTS and DEFAULT_BREAKPOINT_COLS if you want to extend them):

const { cols, layout, onLayoutChange } = useResponsiveLayout(() => ({ width: width.value, layouts: layouts.value, breakpoints: { lg: 1280, md: 900, sm: 0 }, cols: { lg: 16, md: 8, sm: 4 }, onLayoutChange: (_active, all) => (layouts.value = all), }));

onLayoutChange gives you both

The composable’s onLayoutChange option is (activeLayout, allLayouts): the layout for the current breakpoint and the full updated map. Store the map so edits at one breakpoint don’t clobber the others:

onLayoutChange: (_active, all) => (layouts.value = all),

The onLayoutChange the composable returns (which you pass to useGridContainer) takes care of routing each committed change into the active breakpoint’s entry before calling your option above. It’s a plain function, not a ref — pass it straight through.

Prefer the turnkey component? <ResponsiveGridLayout> is the component that wraps this composable — pass it layouts, width, and an item scoped slot and it handles the provider and the grid. See Components for its full prop table.

Last updated on