Menu

Tailwind CSS Grid Auto Flow

Grid Auto Flow controls how elements automatically populate grid areas, especially when explicit row or column placements are not specified. It helps determine whether elements should flow by row, by column, or by filling any remaining cells in a dense manner.

Tailwind CSS provides a list of utility classes to implement all these behaviors. Whether you are new to Tailwind or an experienced user, the following sections will guide you through crucial details about integrating grid auto flow into your projects.

ClassPropertiesExample
grid-flow-rowgrid-auto-flow: row;<div className="grid-flow-row"></div>
grid-flow-colgrid-auto-flow: column;<div className="grid-flow-col"></div>
grid-flow-densegrid-auto-flow: dense;<div className="grid-flow-dense"></div>
grid-flow-row-densegrid-auto-flow: row dense;<div className="grid-flow-row-dense"></div>
grid-flow-col-densegrid-auto-flow: column dense;<div className="grid-flow-col-dense"></div>

Overview of Grid Auto Flow

Adding the Grid Auto Flow

When designing grid layouts, the focus is typically on defining rows and columns. If extra items exceed the grid, they are placed in the next available row by default.

The grid-flow-* utilities let you control how these extra items are positioned. If a row-based layout isn’t ideal, you can arrange them in columns or use a dense pattern to optimize space.

This is a live editor. Play around with it!
export default function RowFlowGallery() {
  return (
    <div className="grid grid-cols-2 grid-rows-2 grid-flow-col gap-4 bg-gray-100 p-8 gap-4 h-screen">
      <div className="bg-blue-300 h-24 w-24" />
      <div className="bg-red-300 h-24 w-24" />
      <div className="bg-green-300 h-24 w-24" />
      <div className="bg-pink-300 h-24 w-24" />
      <div className="bg-amber-300 h-24 w-24" />
      <div className="bg-indigo-300 h-24 w-24" />
    </div>
  );
}

States and Responsiveness

Hover and Focus States

Tailwind allows you to conditionally apply the grid-auto-flow on certain states like hover and focus. Use Tailwind's state modifiers like- hover, focus, etc. to apply the utility only when these states are active, e.g., hover:grid-flow-col.

This is a live editor. Play around with it!
export default function HoverFlowGrid() {
  return (
    <div className="h-screen w-screen">
      <p className="underline px-10 text-center pt-6">Hover on the below container to change the <code>grid-auto-flow</code></p>
      <div className="grid grid-cols-2 grid-rows-2 hover:grid-flow-col gap-4 bg-lime-100 p-8 gap-4 h-72 mt-10">
        <div className="bg-blue-300 h-16 w-16" />
        <div className="bg-red-300 h-16 w-16" />
        <div className="bg-green-300 h-16 w-16" />
        <div className="bg-pink-300 h-16 w-16" />
        <div className="bg-amber-300 h-16 w-16" />
        <div className="bg-indigo-300 h-16 w-16" />
      </div>
    </div>
  );
}

Breakpoint Modifiers

Tailwind CSS provides breakpoint modifiers to conditionally apply the grid-auto-flow only when the screen hits the defined breakpoint. Use Tailwind's breakpoint modifiers like- sm, md, etc., to apply the utility only on these breakpoints and above.

This is a live editor. Play around with it!
export default function ResponsiveAutoFlow() {
  return (
    <div className="h-screen w-screen">
      <p className="underline px-6 text-center pt-6">The below container will change the <code>grid-auto-flow</code> on the <code>md</code> breakpoint</p>
      <div className="grid grid-cols-2 grid-rows-2 md:grid-flow-col gap-4 bg-lime-100 p-8 gap-4 h-72 mt-10">
        <div className="bg-blue-300 h-16 w-16" />
        <div className="bg-red-300 h-16 w-16" />
        <div className="bg-green-300 h-16 w-16" />
        <div className="bg-pink-300 h-16 w-16" />
        <div className="bg-amber-300 h-16 w-16" />
        <div className="bg-indigo-300 h-16 w-16" />
      </div>
    </div>
  );
}

Real World Examples

A responsive product gallery layout that automatically flows items into available spaces.

This is a live editor. Play around with it!
const ProductGallery = () => {
  const products = [
    {
      id: 1,
      title: "Leather Backpack",
      price: "$89.99",
      featured: true,
      src: "https://images.unsplash.com/photo-1548036328-c9fa89d128fa",
      alt: "Brown leather backpack"
    },
    {
      id: 2,
      title: "Canvas Tote",
      price: "$29.99",
      featured: false,
      src: "https://images.unsplash.com/photo-1544816155-12df9643f363",
      alt: "Beige canvas tote bag"
    },
    {
      id: 3,
      title: "Messenger Bag",
      price: "$59.99",
      featured: true,
      src: "https://images.unsplash.com/photo-1590874103328-eac38a683ce7",
      alt: "Black messenger bag"
    },
    {
      id: 4,
      title: "Laptop Sleeve",
      price: "$39.99",
      featured: false,
      src: "https://images.unsplash.com/photo-1525973132219-a04334a76080",
      alt: "Gray laptop sleeve"
    },
    {
      id: 5,
      title: "Duffel Bag",
      price: "$79.99",
      featured: true,
      src: "https://images.unsplash.com/photo-1553062407-98eeb64c6a62",
      alt: "Navy duffel bag"
    },
    {
      id: 6,
      title: "Mini Purse",
      price: "$45.99",
      featured: false,
      src: "https://images.unsplash.com/photo-1584917865442-de89df76afd3",
      alt: "Pink mini purse"
    }
  ];

  return (
    <div className="p-4 bg-gray-50">
      <div className="grid grid-cols-2 gap-2 grid-flow-dense">
        {products.map(product => (
          <div 
            key={product.id}
            className={`rounded-lg overflow-hidden shadow-sm bg-white
              ${product.featured ? 'col-span-2' : ''}`}
          >
            <img 
              src={product.src} 
              alt={product.alt}
              className="w-full h-32 object-cover"
            />
            <div className="p-2">
              <h4 className="text-sm font-semibold">{product.title}</h4>
              <p className="text-xs text-gray-600">{product.price}</p>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
};

export default ProductGallery;

Blog Post Grid

A blog post preview grid that automatically arranges posts based on their importance.

This is a live editor. Play around with it!
const BlogGrid = () => {
  const posts = [
    {
      id: 1,
      title: "The Future of AI",
      category: "Technology",
      readTime: "5 min",
      src: "https://images.unsplash.com/photo-1677442136019-21780ecad995",
      alt: "AI visualization"
    },
    {
      id: 2,
      title: "Sustainable Living",
      category: "Lifestyle",
      readTime: "3 min",
      src: "https://images.unsplash.com/photo-1472141521881-95d0e87e2e39",
      alt: "Eco-friendly home"
    },
    {
      id: 3,
      title: "Remote Work Tips",
      category: "Work",
      readTime: "4 min",
      src: "https://images.unsplash.com/photo-1587614382346-4ec70e388b28",
      alt: "Home office setup"
    },
    {
      id: 4,
      title: "Healthy Recipes",
      category: "Food",
      readTime: "6 min",
      src: "https://images.unsplash.com/photo-1490645935967-10de6ba17061",
      alt: "Healthy meal"
    },
    {
      id: 5,
      title: "Fitness at Home",
      category: "Health",
      readTime: "4 min",
      src: "https://images.unsplash.com/photo-1518611012118-696072aa579a",
      alt: "Home workout"
    },
    {
      id: 6,
      title: "Travel Guide",
      category: "Travel",
      readTime: "5 min",
      src: "https://images.unsplash.com/photo-1488646953014-85cb44e25828",
      alt: "Travel destination"
    }
  ];

  return (
    <div className="p-3 bg-white">
      <div className="grid grid-rows-2 grid-flow-col auto-cols-[180px] gap-3 overflow-x-auto">
        {posts.map(post => (
          <div key={post.id} className="rounded-lg bg-gray-50 shadow-sm">
            <img 
              src={post.src} 
              alt={post.alt}
              className="w-full h-24 object-cover rounded-t-lg"
            />
            <div className="p-2">
              <span className="text-xs text-blue-600">{post.category}</span>
              <h4 className="text-sm font-medium mt-1">{post.title}</h4>
              <p className="text-xs text-gray-500 mt-1">{post.readTime} read</p>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
};

export default BlogGrid;

A photo gallery that uses dense auto flow to create a masonry-like layout with varying image sizes.

This is a live editor. Play around with it!
const PhotoGallery = () => {
  const photos = [
    {
      id: 1,
      size: 'large',
      src: "https://images.unsplash.com/photo-1469474968028-56623f02e42e",
      alt: "Nature landscape"
    },
    {
      id: 2,
      size: 'small',
      src: "https://images.unsplash.com/photo-1426604966848-d7adac402bff",
      alt: "Mountain view"
    },
    {
      id: 3,
      size: 'medium',
      src: "https://images.unsplash.com/photo-1472214103451-9374bd1c798e",
      alt: "Forest path"
    },
    {
      id: 4,
      size: 'small',
      src: "https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05",
      alt: "Sunset view"
    },
    {
      id: 5,
      size: 'large',
      src: "https://images.unsplash.com/photo-1441974231531-c6227db76b6e",
      alt: "Waterfall"
    },
    {
      id: 6,
      size: 'medium',
      src: "https://images.unsplash.com/photo-1497436072909-60f360e1d4b1",
      alt: "Mountain lake"
    }
  ];

  return (
    <div className="p-2 bg-black">
      <div className="grid grid-cols-3 gap-2 grid-flow-dense">
        {photos.map(photo => (
          <div 
            key={photo.id}
            className={`
              ${photo.size === 'large' ? 'col-span-2 row-span-2' : ''}
              ${photo.size === 'medium' ? 'col-span-2' : ''}
              rounded overflow-hidden
            `}
          >
            <img 
              src={photo.src} 
              alt={photo.alt}
              className="w-full h-full object-cover"
            />
          </div>
        ))}
      </div>
    </div>
  );
};

export default PhotoGallery;

Task Board Column Flow

A task board with a horizontal scrolling list of task columns.

This is a live editor. Play around with it!
const TaskBoard = () => {
  const tasks = [
    {
      id: 1,
      title: "Design System",
      priority: "High",
      assignee: "Alice",
      status: "In Progress"
    },
    {
      id: 2,
      title: "API Integration",
      priority: "Medium",
      assignee: "Bob",
      status: "Todo"
    },
    {
      id: 3,
      title: "User Testing",
      priority: "Low",
      assignee: "Charlie",
      status: "Done"
    },
    {
      id: 4,
      title: "Documentation",
      priority: "Medium",
      assignee: "David",
      status: "Review"
    },
    {
      id: 5,
      title: "Bug Fixes",
      priority: "High",
      assignee: "Eve",
      status: "In Progress"
    },
    {
      id: 6,
      title: "Feature Release",
      priority: "High",
      assignee: "Frank",
      status: "Todo"
    }
  ];

  return (
    <div className="p-3 bg-gray-100 h-screen">
      <div className="grid grid-rows-2 grid-flow-col auto-cols-[200px] gap-3 overflow-x-auto">
        {['Todo', 'In Progress', 'Review', 'Done'].map(status => (
          <div key={status} className="bg-white rounded-lg p-2 shadow-sm">
            <h4 className="text-sm font-semibold mb-2">{status}</h4>
            <div className="space-y-2">
              {tasks
                .filter(task => task.status === status)
                .map(task => (
                  <div key={task.id} className="bg-gray-50 p-2 rounded">
                    <h5 className="text-xs font-medium">{task.title}</h5>
                    <div className="flex items-center mt-1">
                      <span className={`text-xs px-1.5 rounded ${
                        task.priority === 'High' ? 'bg-red-100 text-red-600' :
                        task.priority === 'Medium' ? 'bg-yellow-100 text-yellow-600' :
                        'bg-green-100 text-green-600'
                      }`}>
                        {task.priority}
                      </span>
                      <span className="text-xs text-gray-500 ml-2">{task.assignee}</span>
                    </div>
                  </div>
                ))}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
};

export default TaskBoard;

Calendar Event Grid

A calendar event grid to display events with different durations and priority levels efficiently.

This is a live editor. Play around with it!
const CalendarEvents = () => {
  const events = [
    {
      id: 1,
      title: "Team Meeting",
      time: "9:00 AM",
      duration: "long",
      priority: "high",
      attendees: 8,
      location: "Conference Room A"
    },
    {
      id: 2,
      title: "Client Call",
      time: "11:00 AM",
      duration: "short",
      priority: "medium",
      attendees: 3,
      location: "Zoom"
    },
    {
      id: 3,
      title: "Product Demo",
      time: "2:00 PM",
      duration: "medium",
      priority: "high",
      attendees: 12,
      location: "Main Hall"
    },
    {
      id: 4,
      title: "Quick Sync",
      time: "3:30 PM",
      duration: "short",
      priority: "low",
      attendees: 2,
      location: "Slack Huddle"
    },
    {
      id: 5,
      title: "Workshop",
      time: "4:00 PM",
      duration: "long",
      priority: "medium",
      attendees: 15,
      location: "Training Room"
    },
    {
      id: 6,
      title: "Review Session",
      time: "5:30 PM",
      duration: "medium",
      priority: "high",
      attendees: 6,
      location: "Meeting Room B"
    }
  ];

  return (
    <div className="p-3 bg-gray-50">
      <div className="grid grid-cols-3 gap-2 grid-flow-dense">
        {events.map(event => (
          <div 
            key={event.id}
            className={`
              bg-white rounded-lg p-2 shadow-sm
              ${event.duration === 'long' ? 'col-span-2 row-span-2' : ''}
              ${event.duration === 'medium' ? 'col-span-2' : ''}
              ${event.priority === 'high' ? 'border-l-4 border-red-400' : 
                event.priority === 'medium' ? 'border-l-4 border-yellow-400' : 
                'border-l-4 border-green-400'}
            `}
          >
            <div className="flex justify-between items-start">
              <h3 className="text-sm font-medium">{event.title}</h3>
              <span className="text-xs bg-gray-100 px-2 py-0.5 rounded">
                {event.time}
              </span>
            </div>
            <div className="mt-2 space-y-1">
              <div className="flex items-center text-xs text-gray-600">
                <svg className="w-3 h-3 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
                </svg>
                {event.location}
              </div>
              <div className="flex items-center text-xs text-gray-600">
                <svg className="w-3 h-3 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
                </svg>
                {event.attendees} attendees
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
};

export default CalendarEvents;

Best Practices

Maintain Design Consistency

When working with grid-flow-* utilities, aim to standardize the visual weight and alignment of components. This approach enhances the user’s experience by keeping each area of the interface predictable, especially when there are repeated elements such as product cards or profile listings.

A major contributor to consistent layouts is adopting similar spacing, colors, and typography choices. For example, if you are using grid-flow-dense, match the same vertical and horizontal spacing for both large and small items. That way, users can focus on the content rather than being distracted by uneven gaps or irregular layouts.

Build Responsive Design

Responsiveness is the backbone of modern web interfaces, and grid-auto-flow can intelligently adapt to a wide variety of screen sizes. By default, smaller devices often benefit from a row-based flow, ensuring each item is fully visible without excessive scrolling. As screens widen, you can transition to a column-based or dense arrangement, taking advantage of horizontal space while preserving clarity.

Tailwind’s responsive variants allow you to set distinct auto flow behaviors at key widths. For instance, you can start with a straightforward grid-flow-row on phones, switch to md:grid-flow-col on tablets, then adopt lg:grid-flow-col-dense on larger desktops. Each shift can be refined with gap, col-span, or row-span classes to handle special circumstances.

Accessibility Considerations

Enhance Readability and Navigability

Clarity is paramount when designing layouts, and grid-auto-flow can impact how individuals prioritize and navigate content. Dense flow, for instance, may lead to certain blocks shifting into positions that deviate from the typical linear reading order. While this can maximize space usage, it also has the potential to disrupt reading continuity for users who rely on predictable structures.

To boost readability, aim for logical grouping of elements. If your layout automatically aligns items in a column-dense manner, ensure that related pieces of information remain in close proximity. Additionally, keep margins or borders consistent so that each grid area is distinguishable.

Focus on High Contrast

High contrast is vital for users with visual impairments or low-vision conditions. Even if your design arranges grids automatically, appropriate text and background contrasts should remain consistent. With grid items shifting positions, you want to guarantee that every piece of text or interactive element is legible, regardless of how the layout rearranges them.

Adopt a color palette that universally satisfies contrast ratios. If you are using pastel backgrounds for your grids, ensure the text or icons placed on these backgrounds are dark enough for visibility. Conversely, if your brand guidelines incorporate a dark background, pick a sufficiently bright text color to meet or exceed recommended WCAG contrast levels. Because grid-flow-dense might relocate certain items, do not assume any single item will always be surrounded by the same hue.