πŸš€ DAY 5 OF LAUNCH WEEK: Introducing Socket Firewall Enterprise.Learn more β†’
Socket
Book a DemoInstallSign in
Socket

tailwind-grid-layout

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tailwind-grid-layout

A modern, Tailwind CSS-based grid layout system for React. A lightweight alternative to react-grid-layout with full feature parity.

latest
Source
npmnpm
Version
1.0.1
Version published
Maintainers
1
Created
Source

Tailwind Grid Layout

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.

npm version license bundle size

Version 0.1.0 - First stable release

English | ν•œκ΅­μ–΄

Features

  • 🎯 Full Feature Parity with react-grid-layout
  • πŸͺΆ Lightweight - Smaller bundle size using Tailwind CSS
  • 🎨 Tailwind Native - Built with Tailwind CSS utilities
  • πŸ“± Responsive - Works on all screen sizes
  • πŸ“± Mobile Touch - Full touch device optimization with enhanced gesture support
    • Improved touch point accuracy
    • Long press gesture support
    • Prevent scroll-drag conflicts
    • Multi-touch prevention for stability
  • πŸ”§ TypeScript - Full TypeScript support
  • ⚑ Performance - Optimized rendering and animations
  • πŸ§ͺ Well Tested - 100% test coverage

Installation

npm install tailwind-grid-layout
# or
yarn add tailwind-grid-layout
# or
pnpm add tailwind-grid-layout

Prerequisites

  • React 19.1.0
  • Tailwind CSS 4.1.8+ (v4 only - CSS-first configuration)
  • Node.js 20.0.0+
  • pnpm 10.11.0+

Tailwind CSS v4 Setup

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);
}

Quick Start

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>
  )
}

Testing

# Run tests
pnpm test

# Watch mode
pnpm test:watch

# Coverage report
pnpm test:coverage

Test Coverage

This library maintains 100% test coverage:

  • βœ… Lines: 100%
  • βœ… Statements: 100%
  • βœ… Functions: 100%
  • βœ… Branches: 100%

Props Reference

GridContainer Props

PropTypeDefaultDescription
itemsGridItem[]requiredArray of grid items with position and size
children(item: GridItem) => ReactNoderequiredRender function for grid items
colsnumber12Number of columns in the grid
rowHeightnumber60Height of each row in pixels
gapnumber16Gap 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]
maxRowsnumber-Maximum number of rows
isDraggablebooleantrueEnable/disable dragging
isResizablebooleantrueEnable/disable resizing
preventCollisionbooleanfalsePrevent items from colliding
allowOverlapbooleanfalseAllow items to overlap
isBoundedbooleantrueKeep items within container bounds
compactType'vertical' | 'horizontal' | null'vertical'Compaction type
resizeHandlesArray<'s' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne'>['se']Resize handle positions
draggableCancelstring-CSS selector for elements that should not trigger drag
draggableHandlestring-CSS selector for drag handle
autoSizebooleantrueContainer height adjusts to fit all items
verticalCompactbooleantrueDEPRECATED: Use compactType
transformScalenumber1Scale factor for drag/resize when zoomed
droppingItemPartial<GridItem>-Preview item while dragging from outside
classNamestring-Additional CSS classes for the container
styleReact.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

GridItem Properties

PropertyTypeRequiredDescription
idstringβœ“Unique identifier for the item
xnumberβœ“X position in grid units
ynumberβœ“Y position in grid units
wnumberβœ“Width in grid units
hnumberβœ“Height in grid units
minWnumber-Minimum width
minHnumber-Minimum height
maxWnumber-Maximum width
maxHnumber-Maximum height
isDraggableboolean-Override container's isDraggable
isResizableboolean-Override container's isResizable
staticboolean-Make item static (unmovable/unresizable)
classNamestring-Additional CSS classes for the item

ResponsiveGridContainer Props

PropTypeDefaultDescription
layoutsBreakpointLayoutsrequiredObject 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
widthnumber-Container width (provided by WidthProvider)
...GridContainerProps--All other GridContainer props except items, cols, onLayoutChange

Comparison with react-grid-layout

Featurereact-grid-layouttailwind-grid-layoutNotes
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
DependenciesReact onlyReact + Tailwind
Tree-shakingβœ…βœ…Full support

Advanced Examples

With Custom Drag Handle

<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>

Static Items

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 },
]

Responsive Breakpoints

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>
  )
}

Drag and Drop from Outside

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>

Custom Resize Handles

<GridContainer
  items={items}
  resizeHandles={['se', 'sw', 'ne', 'nw']} // Enable corner handles only
>
  {(item) => <div>Item {item.id}</div>}
</GridContainer>

Prevent Collision

<GridContainer
  items={items}
  preventCollision={true} // Items cannot overlap
  allowOverlap={false}
>
  {(item) => <div>Item {item.id}</div>}
</GridContainer>

Bounded Grid with Max Rows

<GridContainer
  items={items}
  isBounded={true}
  maxRows={10}
>
  {(item) => <div>Item {item.id}</div>}
</GridContainer>

AutoSize Container

<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>

Real-time Responsive Updates

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>
    </>
  )
}

Dropping Item Preview

<DroppableGridContainer
  items={items}
  droppingItem={{ w: 4, h: 2 }} // Shows preview while dragging
  onDrop={(newItem) => setItems([...items, newItem])}
>
  {(item) => <div>Item {item.id}</div>}
</DroppableGridContainer>

Layout Utilities

generateLayouts

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)

generateResponsiveLayouts

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
})

WidthProvider HOC

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

Styling Guide

Using with Tailwind CSS

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>

Custom Placeholders

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);
}

Performance Optimizations

  • Hardware Acceleration: Uses CSS transforms with will-change
  • Gesture Debouncing: Optimized touch event handling
    • Touch events are debounced at 16ms (60fps)
    • Minimizes unnecessary re-renders
  • Memory Management: Proper cleanup of event listeners
  • Bundle Splitting: Tree-shakable exports
  • ResizeObserver: Efficient container width detection
  • Animation Control: Transitions disabled during interactions

Touch Event Handling

Optimized touch event handling for best performance on mobile devices:

  • Passive Listeners: Uses passive touch events for improved scroll performance
  • Gesture Recognition: Accurately distinguishes between tap, long press, and drag gestures
  • Momentum Scrolling: Natural momentum effects after touch release
  • Pointer Events API: Unified handling for touch, mouse, and pen input

Browser Support

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Edge (latest)
  • Mobile Safari (iOS 12+)
  • Chrome Mobile (Android 7+)
  • ResizeObserver support required for optimal performance

Contributing

We welcome contributions! Please see our Contributing Guide for details.

License

MIT Β© Seungwoo, Lee

Acknowledgments

This library is inspired by react-grid-layout and aims to provide a modern, Tailwind-first alternative.

Keywords

react

FAQs

Package last updated on 24 Jun 2025

Did you know?

Socket

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.

Install

Related posts