
Product
Introducing Socket Firewall Enterprise: Flexible, Configurable Protection for Modern Package Ecosystems
Socket Firewall Enterprise is now available with flexible deployment, configurable policies, and expanded language support.
tailwind-grid-layout
Advanced tools
A modern, Tailwind CSS-based grid layout system for React. A lightweight alternative to react-grid-layout with full feature parity.
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.
Version 0.1.0 - First stable release
English | νκ΅μ΄
npm install tailwind-grid-layout
# or
yarn add tailwind-grid-layout
# or
pnpm add tailwind-grid-layout
This library requires Tailwind CSS v4 with its new CSS-first configuration approach. No JavaScript configuration file is needed.
/* In your main CSS file */
@import "tailwindcss";
/* Optional: Add custom theme configuration */
@theme {
--color-grid-placeholder: oklch(0.7 0.15 210);
--color-grid-handle: oklch(0.3 0.05 210);
}
import { GridContainer } from 'tailwind-grid-layout'
const items = [
{ id: '1', x: 0, y: 0, w: 2, h: 2 },
{ id: '2', x: 2, y: 0, w: 2, h: 2 },
{ id: '3', x: 0, y: 2, w: 4, h: 2 }
]
function App() {
return (
<GridContainer
items={items}
cols={12}
rowHeight={60}
onLayoutChange={(newLayout) => console.log(newLayout)}
>
{(item) => (
<div className="bg-blue-500 text-white p-4 rounded">
Item {item.id}
</div>
)}
</GridContainer>
)
}
# Run tests
pnpm test
# Watch mode
pnpm test:watch
# Coverage report
pnpm test:coverage
This library maintains 100% test coverage:
| Prop | Type | Default | Description |
|---|---|---|---|
| items | GridItem[] | required | Array of grid items with position and size |
| children | (item: GridItem) => ReactNode | required | Render function for grid items |
| cols | number | 12 | Number of columns in the grid |
| rowHeight | number | 60 | Height of each row in pixels |
| gap | number | 16 | Gap between grid items in pixels |
| margin | [number, number] | [gap, gap] | Margin between items [horizontal, vertical] |
| containerPadding | [number, number] | [16, 16] | Padding inside the grid container [horizontal, vertical] |
| maxRows | number | - | 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 type |
| resizeHandles | Array<'s' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne'> | ['se'] | Resize handle positions |
| draggableCancel | string | - | CSS selector for elements that should not trigger drag |
| draggableHandle | string | - | CSS selector for drag handle |
| autoSize | boolean | true | Container height adjusts to fit all items |
| verticalCompact | boolean | true | DEPRECATED: Use compactType |
| transformScale | number | 1 | Scale factor for drag/resize when zoomed |
| droppingItem | Partial<GridItem> | - | Preview item while dragging from outside |
| className | string | - | Additional CSS classes for the container |
| style | React.CSSProperties | - | Inline styles for the container |
| onLayoutChange | (layout: GridItem[]) => void | - | Callback when layout changes |
| onDragStart | (layout, oldItem, newItem, placeholder, e, element) => void | - | Drag start callback |
| onDrag | (layout, oldItem, newItem, placeholder, e, element) => void | - | Drag callback |
| onDragStop | (layout, oldItem, newItem, placeholder, e, element) => void | - | Drag stop callback |
| onResizeStart | (layout, oldItem, newItem, placeholder, e, element) => void | - | Resize start callback |
| onResize | (layout, oldItem, newItem, placeholder, e, element) => void | - | Resize callback |
| onResizeStop | (layout, oldItem, newItem, placeholder, e, element) => void | - | Resize stop callback |
| Property | Type | Required | Description |
|---|---|---|---|
| id | string | β | Unique identifier for the item |
| x | number | β | X position in grid units |
| y | number | β | Y position in grid units |
| w | number | β | Width in grid units |
| h | number | β | Height in grid units |
| minW | number | - | Minimum width |
| minH | number | - | Minimum height |
| maxW | number | - | Maximum width |
| maxH | number | - | Maximum height |
| isDraggable | boolean | - | Override container's isDraggable |
| isResizable | boolean | - | Override container's isResizable |
| static | boolean | - | Make item static (unmovable/unresizable) |
| className | string | - | Additional CSS classes for the item |
| Prop | Type | Default | Description |
|---|---|---|---|
| layouts | BreakpointLayouts | required | Object with layouts for each breakpoint |
| breakpoints | { [breakpoint: string]: number } | { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 } | Minimum widths for each breakpoint |
| cols | { [breakpoint: string]: number } | { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 } | Number of columns for each breakpoint |
| onLayoutChange | (layout: GridItem[], layouts: BreakpointLayouts) => void | - | Called when layout changes with current layout and all layouts |
| onBreakpointChange | (newBreakpoint: string, cols: number) => void | - | Called when breakpoint changes |
| width | number | - | Container width (provided by WidthProvider) |
| ...GridContainerProps | - | - | All other GridContainer props except items, cols, onLayoutChange |
| Feature | react-grid-layout | tailwind-grid-layout | Notes |
|---|---|---|---|
| Core Features | |||
| Drag & Drop | β | β | Full support |
| Resize | β | β | 8-direction resize |
| Collision Detection | β | β | 50% overlap rule |
| Auto-compaction | β | β | Vertical, horizontal, or none |
| Static Items | β | β | Full support |
| Bounded Movement | β | β | Keep items in bounds |
| Layout Options | |||
| Responsive Breakpoints | β | β | Real-time responsive layouts with ResizeObserver |
| Persist Layout | β | β | Via onLayoutChange |
| Min/Max Dimensions | β | β | Full support |
| Prevent Collision | β | β | Full support |
| Allow Overlap | β | β | Full support |
| Events | |||
| Layout Change | β | β | Full support |
| Drag Events | β | β | Start, move, stop |
| Resize Events | β | β | Start, resize, stop |
| Drop from Outside | β | β | Full support with DroppableGridContainer |
| Styling | |||
| CSS-in-JS | β | β | Uses Tailwind |
| Custom Classes | β | β | Full support |
| Animations | β | β | Tailwind transitions |
| Performance | |||
| Bundle Size | ~30KB | ~22KB (gzip) | Smaller bundle |
| Dependencies | React only | React + Tailwind | |
| Tree-shaking | β | β | Full support |
<GridContainer items={items}>
{(item) => (
<div className="bg-white rounded-lg shadow p-4">
<div className="cursor-move p-2 bg-gray-100 rounded" data-drag-handle>
<GripIcon className="w-4 h-4" />
</div>
<div className="p-4">
Content for {item.id}
</div>
</div>
)}
</GridContainer>
const items = [
{ id: '1', x: 0, y: 0, w: 4, h: 2, static: true }, // This item cannot be moved
{ id: '2', x: 4, y: 0, w: 4, h: 2 },
]
import { ResponsiveGridContainer, WidthProvider } from 'tailwind-grid-layout'
// Define layouts for each breakpoint
const layouts = {
lg: [
{ id: '1', x: 0, y: 0, w: 6, h: 2 },
{ id: '2', x: 6, y: 0, w: 6, h: 2 },
{ id: '3', x: 0, y: 2, w: 4, h: 2 },
{ id: '4', x: 4, y: 2, w: 8, h: 2 }
],
md: [
{ id: '1', x: 0, y: 0, w: 10, h: 2 },
{ id: '2', x: 0, y: 2, w: 10, h: 2 },
{ id: '3', x: 0, y: 4, w: 5, h: 2 },
{ id: '4', x: 5, y: 4, w: 5, h: 2 }
],
sm: [
{ id: '1', x: 0, y: 0, w: 6, h: 2 },
{ id: '2', x: 0, y: 2, w: 6, h: 2 },
{ id: '3', x: 0, y: 4, w: 6, h: 2 },
{ id: '4', x: 0, y: 6, w: 6, h: 2 }
],
xs: [
{ id: '1', x: 0, y: 0, w: 4, h: 2 },
{ id: '2', x: 0, y: 2, w: 4, h: 2 },
{ id: '3', x: 0, y: 4, w: 4, h: 2 },
{ id: '4', x: 0, y: 6, w: 4, h: 2 }
],
xxs: [
{ id: '1', x: 0, y: 0, w: 2, h: 2 },
{ id: '2', x: 0, y: 2, w: 2, h: 2 },
{ id: '3', x: 0, y: 4, w: 2, h: 2 },
{ id: '4', x: 0, y: 6, w: 2, h: 2 }
]
}
// Option 1: Manual width tracking
function ResponsiveExample() {
const [currentBreakpoint, setCurrentBreakpoint] = useState('lg')
return (
<ResponsiveGridContainer
layouts={layouts}
onBreakpointChange={(breakpoint) => {
setCurrentBreakpoint(breakpoint)
console.log(`Switched to ${breakpoint} breakpoint`)
}}
onLayoutChange={(layout, allLayouts) => {
// Save layouts to state or backend
console.log('Layout changed:', allLayouts)
}}
>
{(item) => (
<div className="bg-blue-500 text-white p-4 rounded">
Item {item.id}
</div>
)}
</ResponsiveGridContainer>
)
}
// Option 2: Using WidthProvider for automatic width detection
const ResponsiveGridWithWidth = WidthProvider(ResponsiveGridContainer)
function App() {
return (
<ResponsiveGridWithWidth
layouts={layouts}
// Custom breakpoints (optional)
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
// Custom column configuration (optional)
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
>
{(item) => <div>Item {item.id}</div>}
</ResponsiveGridWithWidth>
)
}
import { DroppableGridContainer } from 'tailwind-grid-layout'
<DroppableGridContainer
items={items}
onDrop={(newItem) => setItems([...items, newItem])}
droppingItem={{ w: 2, h: 2 }} // Default size for dropped items
>
{(item) => <div>Dropped Item {item.id}</div>}
</DroppableGridContainer>
<GridContainer
items={items}
resizeHandles={['se', 'sw', 'ne', 'nw']} // Enable corner handles only
>
{(item) => <div>Item {item.id}</div>}
</GridContainer>
<GridContainer
items={items}
preventCollision={true} // Items cannot overlap
allowOverlap={false}
>
{(item) => <div>Item {item.id}</div>}
</GridContainer>
<GridContainer
items={items}
isBounded={true}
maxRows={10}
>
{(item) => <div>Item {item.id}</div>}
</GridContainer>
<GridContainer
items={items}
autoSize={true} // Container height adjusts automatically
>
{(item) => <div>Item {item.id}</div>}
</GridContainer>
// With fixed height
<div style={{ height: 400, overflow: 'auto' }}>
<GridContainer
items={items}
autoSize={false}
style={{ height: '100%' }}
>
{(item) => <div>Item {item.id}</div>}
</GridContainer>
</div>
The responsive grid automatically updates layouts when the window is resized, with debounced handling for optimal performance:
import { ResponsiveGridContainer } from 'tailwind-grid-layout'
function DashboardExample() {
const [layouts, setLayouts] = useState({
lg: dashboardLayoutLg,
md: dashboardLayoutMd,
sm: dashboardLayoutSm,
xs: dashboardLayoutXs,
xxs: dashboardLayoutXxs
})
const [currentBreakpoint, setCurrentBreakpoint] = useState('')
const [currentCols, setCurrentCols] = useState(12)
return (
<>
{/* Visual breakpoint indicator */}
<div className="mb-4 p-2 bg-green-100 rounded">
Current: {currentBreakpoint} ({currentCols} columns)
</div>
<ResponsiveGridContainer
layouts={layouts}
onLayoutChange={(layout, allLayouts) => {
setLayouts(allLayouts)
}}
onBreakpointChange={(breakpoint, cols) => {
setCurrentBreakpoint(breakpoint)
setCurrentCols(cols)
}}
rowHeight={100}
gap={16}
containerPadding={[16, 16]}
>
{(item) => (
<Card key={item.id}>
<CardHeader>
<CardTitle>{item.title}</CardTitle>
</CardHeader>
<CardContent>
{item.content}
</CardContent>
</Card>
)}
</ResponsiveGridContainer>
</>
)
}
<DroppableGridContainer
items={items}
droppingItem={{ w: 4, h: 2 }} // Shows preview while dragging
onDrop={(newItem) => setItems([...items, newItem])}
>
{(item) => <div>Item {item.id}</div>}
</DroppableGridContainer>
Generate identical layouts for all breakpoints from a single layout definition.
import { generateLayouts } from 'tailwind-grid-layout'
const items = [
{ id: '1', x: 0, y: 0, w: 4, h: 2 },
{ id: '2', x: 4, y: 0, w: 4, h: 2 }
]
// Creates layouts for lg, md, sm, xs, xxs with identical positioning
const layouts = generateLayouts(items)
Automatically adjust layouts to fit different column counts per breakpoint.
import { generateResponsiveLayouts } from 'tailwind-grid-layout'
const items = [
{ id: '1', x: 0, y: 0, w: 12, h: 2 },
{ id: '2', x: 0, y: 2, w: 6, h: 2 }
]
// Adjusts item widths and positions to fit column constraints
const layouts = generateResponsiveLayouts(items, {
lg: 12,
md: 10,
sm: 6,
xs: 4,
xxs: 2
})
Automatically provides container width to ResponsiveGridContainer using ResizeObserver for optimal performance.
import { ResponsiveGridContainer, WidthProvider } from 'tailwind-grid-layout'
const ResponsiveGridWithWidth = WidthProvider(ResponsiveGridContainer)
// Basic usage
<ResponsiveGridWithWidth
layouts={layouts}
rowHeight={100}
>
{(item) => <div>Item {item.id}</div>}
</ResponsiveGridWithWidth>
// With measureBeforeMount to prevent layout shift on initial render
<ResponsiveGridWithWidth
layouts={layouts}
measureBeforeMount={true}
rowHeight={100}
>
{(item) => <div>Item {item.id}</div>}
</ResponsiveGridWithWidth>
// WidthProvider features:
// - Uses ResizeObserver for efficient width detection
// - Falls back to window resize events if ResizeObserver is unavailable
// - Handles SSR correctly with measureBeforeMount option
// - Debounced resize handling (150ms) for better performance
The library is built to work seamlessly with Tailwind CSS:
<GridContainer items={items} className="bg-gray-50 rounded-lg">
{(item) => (
<div className="bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow">
<div className="p-4">
<h3 className="text-lg font-semibold">Item {item.id}</h3>
</div>
</div>
)}
</GridContainer>
The drag and resize placeholders can be styled via CSS:
/* Drag placeholder */
.tailwind-grid-layout .drag-placeholder {
background: rgba(59, 130, 246, 0.15);
border: 2px dashed rgb(59, 130, 246);
}
/* Resize placeholder */
.tailwind-grid-layout .resize-placeholder {
background: rgba(59, 130, 246, 0.1);
border: 2px dashed rgb(59, 130, 246);
}
Optimized touch event handling for best performance on mobile devices:
We welcome contributions! Please see our Contributing Guide for details.
MIT Β© Seungwoo, Lee
This library is inspired by react-grid-layout and aims to provide a modern, Tailwind-first alternative.
FAQs
A modern, Tailwind CSS-based grid layout system for React. A lightweight alternative to react-grid-layout with full feature parity.
We found that tailwind-grid-layout demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.Β It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Socket Firewall Enterprise is now available with flexible deployment, configurable policies, and expanded language support.

Security News
Open source dashboard CNAPulse tracks CVE Numbering Authoritiesβ publishing activity, highlighting trends and transparency across the CVE ecosystem.

Product
Detect malware, unsafe data flows, and license issues in GitHub Actions with Socketβs new workflow scanning support.