A modern, lightweight grid layout system for React built with Tailwind CSS. A powerful alternative to react-grid-layout with full feature parity and a smaller bundle size.
English | 한국어
- Full Feature Parity with react-grid-layout
- Lightweight - Smaller bundle size (~15KB gzip)
- Tailwind Native - Built with Tailwind CSS v4
- Responsive - Works on all screen sizes
- Mobile Touch - Full touch device support
- TypeScript - Full TypeScript support
- Well Tested - 100% test coverage
npm install tailwind-grid-layout- React 19+
- Tailwind CSS 4.x
import { useState } from 'react'
import { GridContainer } from 'tailwind-grid-layout'
import type { GridItem } from 'tailwind-grid-layout'
const initialItems: GridItem[] = [
{ id: '1', x: 0, y: 0, w: 3, h: 2 },
{ id: '2', x: 3, y: 0, w: 5, h: 3 },
{ id: '3', x: 8, y: 0, w: 4, h: 2 },
]
function App() {
const [items, setItems] = useState(initialItems)
return (
<GridContainer
items={items}
cols={12}
rowHeight={80}
gap={16}
onLayoutChange={setItems}
>
{(item) => (
<div className="bg-white rounded-xl p-4 shadow">
Item {item.id}
</div>
)}
</GridContainer>
)
}Basic grid container with drag and resize support.
<GridContainer
items={items}
cols={12}
rowHeight={80}
gap={16}
isDraggable={true}
isResizable={true}
resizeHandles={['se', 'sw', 'ne', 'nw', 'n', 's', 'e', 'w']}
onLayoutChange={setItems}
>
{(item) => <div>Item {item.id}</div>}
</GridContainer>| Prop | Type | Default | Description |
|---|---|---|---|
items |
GridItem[] |
required | Array of grid items |
children |
(item: GridItem) => ReactNode |
required | Render function for each item |
cols |
number |
12 |
Number of columns |
rowHeight |
number |
60 |
Height of each row in pixels |
gap |
number |
16 |
Gap between items in pixels |
margin |
[number, number] |
[gap, gap] |
Margin [horizontal, vertical] |
containerPadding |
[number, number] |
[16, 16] |
Container padding [horizontal, vertical] |
maxRows |
number |
undefined |
Maximum number of rows |
isDraggable |
boolean |
true |
Enable/disable dragging |
isResizable |
boolean |
true |
Enable/disable resizing |
preventCollision |
boolean |
false |
Prevent items from colliding |
allowOverlap |
boolean |
false |
Allow items to overlap |
isBounded |
boolean |
true |
Keep items within container bounds |
compactType |
'vertical' | 'horizontal' | null |
'vertical' |
Compaction direction |
resizeHandles |
ResizeHandle[] |
['se'] |
Resize handle positions |
draggableCancel |
string |
undefined |
CSS selector for non-draggable elements |
draggableHandle |
string |
undefined |
CSS selector for drag handle |
autoSize |
boolean |
true |
Auto-adjust container height |
preserveInitialHeight |
boolean |
false |
Preserve initial layout height |
transformScale |
number |
1 |
Transform scale for nested transforms |
className |
string |
undefined |
Additional CSS classes |
style |
CSSProperties |
undefined |
Additional inline styles |
| Callback | Type | Description |
|---|---|---|
onLayoutChange |
(layout: GridItem[]) => void |
Called when layout changes |
onDragStart |
(layout, oldItem, newItem, placeholder, e, element) => void |
Called when drag starts |
onDrag |
(layout, oldItem, newItem, placeholder, e, element) => void |
Called during drag |
onDragStop |
(layout, oldItem, newItem, placeholder, e, element) => void |
Called when drag ends |
onResizeStart |
(layout, oldItem, newItem, placeholder, e, element) => void |
Called when resize starts |
onResize |
(layout, oldItem, newItem, placeholder, e, element) => void |
Called during resize |
onResizeStop |
(layout, oldItem, newItem, placeholder, e, element) => void |
Called when resize ends |
Responsive grid with breakpoint-based layouts.
import { ResponsiveGridContainer, WidthProvider } from 'tailwind-grid-layout'
const ResponsiveGrid = WidthProvider(ResponsiveGridContainer)
const layouts = {
lg: [{ id: '1', x: 0, y: 0, w: 6, h: 2 }],
md: [{ id: '1', x: 0, y: 0, w: 10, h: 2 }],
sm: [{ id: '1', x: 0, y: 0, w: 6, h: 2 }],
}
<ResponsiveGrid
layouts={layouts}
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
onLayoutChange={(layout, layouts) => console.log(layouts)}
onBreakpointChange={(breakpoint, cols) => console.log(breakpoint)}
>
{(item) => <div>Item {item.id}</div>}
</ResponsiveGrid>Inherits all GridContainer props except items, cols, and onLayoutChange.
| Prop | Type | Default | Description |
|---|---|---|---|
layouts |
{ [breakpoint: string]: GridItem[] } |
required | Layout for each breakpoint |
breakpoints |
{ [breakpoint: string]: number } |
{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 } |
Breakpoint widths |
cols |
{ [breakpoint: string]: number } |
{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 } |
Columns per breakpoint |
onLayoutChange |
(layout: GridItem[], layouts: BreakpointLayouts) => void |
undefined |
Called when layout changes |
onBreakpointChange |
(breakpoint: string, cols: number) => void |
undefined |
Called when breakpoint changes |
width |
number |
undefined |
Container width (for WidthProvider) |
Grid with external drag-and-drop support.
import { DroppableGridContainer } from 'tailwind-grid-layout'
<DroppableGridContainer
items={items}
droppingItem={{ w: 2, h: 2 }}
onDrop={(newItem) => setItems([...items, newItem])}
>
{(item) => <div>Item {item.id}</div>}
</DroppableGridContainer>Inherits all GridContainer props except onDrop.
| Prop | Type | Default | Description |
|---|---|---|---|
droppingItem |
Partial<GridItem> |
{ w: 2, h: 2 } |
Size of dropping item |
onDrop |
(item: GridItem) => void |
undefined |
Called when item is dropped |
HOC that provides width to ResponsiveGridContainer.
import { ResponsiveGridContainer, WidthProvider } from 'tailwind-grid-layout'
const ResponsiveGrid = WidthProvider(ResponsiveGridContainer)
// Or with options
const ResponsiveGrid = WidthProvider(ResponsiveGridContainer)
<ResponsiveGrid measureBeforeMount={true}>
{/* ... */}
</ResponsiveGrid>| Prop | Type | Default | Description |
|---|---|---|---|
measureBeforeMount |
boolean |
false |
Measure width before mounting |
| Property | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Unique identifier |
x |
number |
Yes | X position in grid units |
y |
number |
Yes | Y position in grid units |
w |
number |
Yes | Width in grid units |
h |
number |
Yes | Height in grid units |
minW |
number |
No | Minimum width |
maxW |
number |
No | Maximum width |
minH |
number |
No | Minimum height |
maxH |
number |
No | Maximum height |
static |
boolean |
No | Make item unmovable and unresizable |
isDraggable |
boolean |
No | Override container's isDraggable |
isResizable |
boolean |
No | Override container's isResizable |
className |
string |
No | Additional CSS class for item |
Available resize handle positions:
| Handle | Position |
|---|---|
n |
North (top center) |
s |
South (bottom center) |
e |
East (right center) |
w |
West (left center) |
ne |
North-East (top right corner) |
nw |
North-West (top left corner) |
se |
South-East (bottom right corner) |
sw |
South-West (bottom left corner) |
import { generateLayouts, generateResponsiveLayouts } from 'tailwind-grid-layout'
// Generate layout from items
const layout = generateLayouts(items)
// Generate responsive layouts
const responsiveLayouts = generateResponsiveLayouts(items, breakpoints, cols)| Feature | react-grid-layout | tailwind-grid-layout |
|---|---|---|
| Bundle Size | ~30KB | ~15KB |
| Tailwind Native | No | Yes |
| TypeScript | Yes | Yes |
| Touch Support | Yes | Yes |
| Responsive | Yes | Yes |
| External CSS | Required | Not needed |
MIT © Seungwoo, Lee
