Tailwind CSS Grid Row
Grid Row in CSS refers to how an item in a grid layout is placed across vertical tracks. By defining explicit row boundaries, you can easily control how many rows an element spans and where it begins or ends within the grid. Tailwind includes a variety of row-based classes that enable you to quickly specify row start and end positions.
In this guide, we will learn how to effectively work with these utilities in Tailwind CSS, their conditional & responsive application, and how to use custom grid-row
utilities.
Class | Properties | Example |
---|---|---|
row-auto | grid-row: auto; | <div className="row-auto"></div> |
row-span-1 | grid-row: span 1 / span 1; | <div className="row-span-1"></div> |
row-span-2 | grid-row: span 2 / span 2; | <div className="row-span-2"></div> |
row-span-3 | grid-row: span 3 / span 3; | <div className="row-span-3"></div> |
row-span-4 | grid-row: span 4 / span 4; | <div className="row-span-4"></div> |
row-span-5 | grid-row: span 5 / span 5; | <div className="row-span-5"></div> |
row-span-6 | grid-row: span 6 / span 6; | <div className="row-span-6"></div> |
row-span-7 | grid-row: span 7 / span 7; | <div className="row-span-7"></div> |
row-span-8 | grid-row: span 8 / span 8; | <div className="row-span-8"></div> |
row-span-9 | grid-row: span 9 / span 9; | <div className="row-span-9"></div> |
row-span-10 | grid-row: span 10 / span 10; | <div className="row-span-10"></div> |
row-span-11 | grid-row: span 11 / span 11; | <div className="row-span-11"></div> |
row-span-12 | grid-row: span 12 / span 12; | <div className="row-span-12"></div> |
row-span-full | grid-row: 1 / -1; | <div className="row-span-full"></div> |
row-start-1 | grid-row-start: 1; | <div className="row-start-1"></div> |
row-start-2 | grid-row-start: 2; | <div className="row-start-2"></div> |
row-start-3 | grid-row-start: 3; | <div className="row-start-3"></div> |
row-start-4 | grid-row-start: 4; | <div className="row-start-4"></div> |
row-start-5 | grid-row-start: 5; | <div className="row-start-5"></div> |
row-start-6 | grid-row-start: 6; | <div className="row-start-6"></div> |
row-start-7 | grid-row-start: 7; | <div className="row-start-7"></div> |
row-start-8 | grid-row-start: 8; | <div className="row-start-8"></div> |
row-start-9 | grid-row-start: 9; | <div className="row-start-9"></div> |
row-start-10 | grid-row-start: 10; | <div className="row-start-10"></div> |
row-start-11 | grid-row-start: 11; | <div className="row-start-11"></div> |
row-start-12 | grid-row-start: 12; | <div className="row-start-12"></div> |
row-start-13 | grid-row-start: 13; | <div className="row-start-13"></div> |
row-start-auto | grid-row-start: auto; | <div className="row-start-auto"></div> |
row-end-1 | grid-row-end: 1; | <div className="row-end-1"></div> |
row-end-2 | grid-row-end: 2; | <div className="row-end-2"></div> |
row-end-3 | grid-row-end: 3; | <div className="row-end-3"></div> |
row-end-4 | grid-row-end: 4; | <div className="row-end-4"></div> |
row-end-5 | grid-row-end: 5; | <div className="row-end-5"></div> |
row-end-6 | grid-row-end: 6; | <div className="row-end-6"></div> |
row-end-7 | grid-row-end: 7; | <div className="row-end-7"></div> |
row-end-8 | grid-row-end: 8; | <div className="row-end-8"></div> |
row-end-9 | grid-row-end: 9; | <div className="row-end-9"></div> |
row-end-10 | grid-row-end: 10; | <div className="row-end-10"></div> |
row-end-11 | grid-row-end: 11; | <div className="row-end-11"></div> |
row-end-12 | grid-row-end: 12; | <div className="row-end-12"></div> |
row-end-13 | grid-row-end: 13; | <div className="row-end-13"></div> |
row-end-auto | grid-row-end: auto; | <div className="row-end-auto"></div> |
Overview of Grid Row
Spanning multiple rows
When you want an element to extend vertically across multiple grid tracks, you can specify a row span. For instance, row-span-2
instructs the element to occupy two rows. This is especially handy if you have content that naturally grows taller than surrounding elements or when you want a highlight section that is visually distinct by spanning a greater vertical height.
export default function RowSpanning() { return ( <div className="w-screen h-screen bg-gray-50 p-8"> <div className="grid grid-cols-3 grid-rows-5 gap-4"> <div className="row-span-5 bg-orange-400 flex items-center justify-center px-2"> {/* row-span-5 => grid-row: span 5 / span 5 */} <p>Extends Down 5 Rows</p> </div> <div className="bg-yellow-200 flex items-center justify-center"> <p className="p-1">One Row High</p> </div> <div className="bg-yellow-300 flex row-span-2 items-center justify-center px-2"> <p>Two rows High</p> </div> <div className="bg-yellow-300 flex items-center justify-center"> <p className="p-1">One Row High</p> </div> <div className="bg-yellow-500 flex items-center justify-center row-span-3"> <p className="p-1">Three Rows High</p> </div> </div> </div> ); }
Adding the start and end lines
For even finer control, Tailwind offers utilities for specifying explicit start and end line numbers. This provides a direct mapping to CSS properties like:
grid-row-start: <line>
grid-row-end: <line>
By assigning row-start-*
and row-end-*
, you don't necessarily have to rely on span. You can explicitly target row lines in the grid, e.g., start at row 1 and end at row 3 (row-start-1 row-end-3
). This precision is invaluable when designing label-like sections or anchoring items in very particular vertical segments.
export default function RowSpanning() { return ( <div className="w-screen h-screen bg-gray-50 p-8"> <div className="grid grid-cols-3 grid-rows-5 gap-4"> <div className="row-start-1 row-end-3 bg-orange-400 flex items-center justify-center px-2"> {/* grid-row-start: 1; grid-row-end: 3 */} <p>Starts at 1 and Ends at 3 </p> </div> <div className="bg-yellow-200 flex items-center justify-center"> <p className="p-1">Default Spanning</p> </div> <div className="bg-yellow-300 flex row-start-2 row-end-5 items-center justify-center px-2"> {/* grid-row-start: 2; grid-row-end: 5 */} <p>Starts at 2 and Ends at 5</p> </div> <div className="bg-yellow-300 flex items-center justify-center"> <p className="p-1">Default Spanning</p> </div> <div className="bg-yellow-500 flex items-center justify-center row-start-3 row-end-5"> {/* grid-row-start: 3; grid-row-end: 5 */} <p className="p-1">Starts at 3 and ends at 5</p> </div> </div> </div> ); }
States and Responsiveness
Hover and Focus States
Tailwind’s modifiers such as hover
and focus
, let you adapt an element’s row positioning upon user interaction. You can, for instance, cause an element to shift its row start on hover or span multiple rows only on focus.
export default function InteractiveGrid() { return ( <div className="w-screen h-screen bg-gray-50 p-6"> <p className="underline px-4 text-center pb-6">Hover on the below items to change their spanning rows</p> <div className="grid grid-cols-3 grid-rows-4 gap-4"> <div className="hover:row-span-4 bg-orange-400 flex items-center justify-center px-2"> {/* On hover => grid-row: span 4 / span 4 */} <p>Hover on this item</p> </div> <div className="bg-yellow-200 flex items-center justify-center"> <p className="p-1"></p> </div> <div className="bg-yellow-300 flex hover:row-span-2 items-center justify-center px-2"> <p>Hover on this item</p> </div> <div className="bg-yellow-300 flex items-center justify-center"> <p className="p-1"></p> </div> <div className="bg-yellow-500 flex items-center justify-center hover:row-span-3"> <p className="p-1">Hover on this item</p> </div> </div> </div> ); }
Breakpoint Modifiers
Tailwind CSS provides breakpoint modifiers to conditionally apply the utility only when the screen hits the defined breakpoint. This is especially helpful for changing the grid-row
only on specific screens. Use Tailwind's breakpoint modifiers like- sm
, md
, etc., to apply the utility only on these breakpoints and above.
export default function ResponsiveGrid() { return ( <div className="w-screen h-screen bg-gray-50 p-6"> <p className="underline px-4 text-center pb-6">The spanning rows of the below items will change as per the screen</p> <div className="grid grid-cols-3 grid-rows-4 gap-4"> <div className="md:row-span-2 bg-orange-400 flex items-center justify-center px-2"> {/* On md => grid-row: span 2 / span 2 */} <p>Row spanning will change on <code>md</code></p> </div> <div className="bg-yellow-200 flex items-center justify-center"> </div> <div className="bg-yellow-300 flex items-center justify-center px-2"> </div> <div className="bg-yellow-300 flex items-center justify-center"> </div> <div className="bg-yellow-500 flex items-center justify-center"> </div> </div> </div> ); }
Custom Grid Row
Extending the Theme
Tailwind’s configuration file (tailwind.config.js
) allows you to extend or override the default theme values for row-based utilities. By customizing the theme, you can define new row spans or line start/end values that match your project’s unique design system.
import tailwindConfig from "./tailwind.config.js"; tailwind.config = tailwindConfig; export default function ExtendedRows() { return ( <div className="w-screen bg-gray-50 p-8"> <p className="underline px-2 text-center pb-6">The below item uses a custom spanning utility</p> <div className="grid grid-cols-3 grid-rows-14 gap-4"> <div className="bg-orange-400 flex items-center justify-center px-2"> <p className="p-1">Default Spanning</p> </div> <div className="bg-yellow-200 flex items-center justify-center"> <p className="p-1">Default Spanning</p> </div> <div className="bg-yellow-300 flex items-center justify-center px-2"> <p className="p-1">Default Spanning</p> </div> <div className="bg-yellow-300 flex items-center justify-center"> <p className="p-1">Default Spanning</p> </div> <div className="bg-yellow-500 flex items-center justify-center row-span-14"> {/* Custom row-span-14 utility */} <p className="p-1">Custom Spanning</p> </div> </div> </div> ); }
Using Arbitrary Values
In addition to theme extension, Tailwind offers arbitrary values for utilities. This is particularly advantageous when you need to quickly experiment with new grid line configurations without editing your theme configuration. Arbitrary values let you specify the property value inline using a square bracket syntax.
export default function ArbitraryValuesDemo() { return ( <div className="w-screen bg-gray-50 p-8"> <p className="underline px-2 text-center pb-6">The below item uses a arbitrary spanning utility</p> <div className="grid grid-cols-3 grid-rows-[repeat(14,minmax(0,1fr))] gap-4"> <div className="bg-orange-400 flex items-center justify-center px-2"> <p className="p-1">Default Spanning</p> </div> <div className="bg-yellow-200 flex items-center justify-center"> <p className="p-1">Default Spanning</p> </div> <div className="bg-yellow-300 flex items-center justify-center px-2"> <p className="p-1">Default Spanning</p> </div> <div className="bg-yellow-300 flex items-center justify-center"> <p className="p-1">Default Spanning</p> </div> <div className="bg-yellow-500 flex items-center justify-center row-[span_14_/_span_14]"> {/* Arbitrary row-span-14 utility */} <p className="p-1">Arbitrary Spanning</p> </div> </div> </div> ); }
Real World Examples
Dynamic Content Feed
A compact social media-style feed with featured posts spanning multiple rows.
const ContentFeed = () => { const posts = [ { id: 1, type: "featured", title: "Mountain Escape", content: "Weekend getaway in the Alps", image: "https://images.unsplash.com/photo-1506905925346-21bda4d32df4", likes: 234, span: "row-span-2", author: "Alex Hiker" }, { id: 2, type: "text", title: "Quick Update", content: "Just landed my dream job!", likes: 89, span: "row-span-1", author: "Sarah Chen" }, { id: 3, type: "featured", title: "Cafe Vibes", content: "Perfect morning coffee spot", image: "https://images.unsplash.com/photo-1495474472287-4d71bcdd2085", likes: 156, span: "row-span-2", author: "Coffee Lover" }, { id: 4, type: "text", title: "Tech News", content: "Latest gadget release today", likes: 45, span: "row-span-1", author: "Tech Insider" }, { id: 5, type: "text", title: "Recipe Share", content: "My grandma's secret pasta", likes: 167, span: "row-span-1", author: "Food Master" }, { id: 6, type: "text", title: "Daily Thought", content: "Embrace the journey", likes: 92, span: "row-span-1", author: "Mindful Soul" } ]; return ( <div className="w-[365px] h-[400px] bg-gray-100 p-2"> <div className="grid grid-cols-2 grid-rows-4 gap-2 h-full"> {posts.map((post) => ( <div key={post.id} className={`${post.span} ${ post.type === 'featured' ? 'col-span-1' : 'col-span-1' } bg-white rounded-lg shadow-sm overflow-hidden`} > {post.type === 'featured' ? ( <div className="h-full flex flex-col"> <div className="h-1/2 relative"> <img src={post.image} alt={post.title} className="w-full h-full object-cover" /> </div> <div className="p-2 flex-1 flex flex-col"> <h3 className="font-bold text-sm">{post.title}</h3> <p className="text-xs text-gray-600 mt-1">{post.content}</p> <div className="mt-auto flex items-center justify-between text-xs"> <span className="text-gray-500">{post.author}</span> <div className="flex items-center gap-1"> <span>♥</span> <span>{post.likes}</span> </div> </div> </div> </div> ) : ( <div className="p-2 h-full flex flex-col"> <h3 className="font-bold text-sm">{post.title}</h3> <p className="text-xs text-gray-600 mt-1">{post.content}</p> <div className="mt-auto flex items-center justify-between text-xs"> <span className="text-gray-500">{post.author}</span> <div className="flex items-center gap-1"> <span>♥</span> <span>{post.likes}</span> </div> </div> </div> )} </div> ))} </div> </div> ); }; export default ContentFeed;
Project Board
A compact project management board with tasks spanning different row heights based on priority and content.
const ProjectBoard = () => { const tasks = [ { id: 1, title: "Website Redesign", priority: "high", dueDate: "Tomorrow", span: "row-span-2", status: "in-progress", assignees: ["Sarah K", "Mike L"], progress: 65, description: "Update homepage layout and implement new brand colors across all pages", type: "featured" }, { id: 2, title: "Bug Fixes", priority: "medium", dueDate: "Today", span: "row-span-1", status: "todo", assignees: ["John D"], type: "normal" }, { id: 3, title: "User Research", priority: "high", dueDate: "Next Week", span: "row-span-2", status: "review", assignees: ["Emma R", "Alex M", "Lisa P"], progress: 80, description: "Conduct user interviews and compile findings report", type: "featured" }, { id: 4, title: "Email Campaign", priority: "low", dueDate: "Friday", span: "row-span-1", status: "todo", assignees: ["Tom S"], type: "normal" }, { id: 5, title: "Team Meeting", priority: "medium", dueDate: "Today", span: "row-span-1", status: "todo", assignees: ["All"], type: "normal" }, { id: 6, title: "Content Review", priority: "low", dueDate: "Thursday", span: "row-span-1", status: "backlog", assignees: ["Kate M"], type: "normal" } ]; const getPriorityColor = (priority) => { switch (priority) { case 'high': return 'bg-red-100 text-red-600'; case 'medium': return 'bg-yellow-100 text-yellow-600'; default: return 'bg-blue-100 text-blue-600'; } }; return ( <div className="w-[365px] h-[400px] bg-gray-100 p-2"> <div className="grid grid-cols-2 grid-rows-4 gap-2 h-full"> {tasks.map((task) => ( <div key={task.id} className={`${task.span} ${ task.type === 'featured' ? 'col-span-1' : 'col-span-1' } bg-white rounded-lg shadow-sm overflow-hidden relative`} > <div className="p-2 h-full flex flex-col"> <div className="flex items-start justify-between mb-2"> <h3 className="font-bold text-sm leading-tight">{task.title}</h3> <span className={`text-xs px-2 py-1 rounded-full ${getPriorityColor(task.priority)}`}> {task.priority} </span> </div> {task.type === 'featured' && ( <> <p className="text-xs text-gray-600 mb-2">{task.description}</p> <div className="mb-2"> <div className="h-1.5 bg-gray-200 rounded-full"> <div className="h-full bg-blue-500 rounded-full" style={{ width: `${task.progress}%` }} ></div> </div> </div> </> )} <div className="mt-auto"> <div className="flex items-center justify-between text-xs"> <span className="text-gray-500">Due {task.dueDate}</span> <div className="flex -space-x-1"> {task.assignees.slice(0, 2).map((assignee, index) => ( <div key={index} className="w-5 h-5 rounded-full bg-gray-200 flex items-center justify-center text-[10px]" > {assignee.charAt(0)} </div> ))} {task.assignees.length > 2 && ( <div className="w-5 h-5 rounded-full bg-gray-300 flex items-center justify-center text-[10px]"> +{task.assignees.length - 2} </div> )} </div> </div> </div> </div> </div> ))} </div> </div> ); }; export default ProjectBoard;
Image Gallery
An image gallery where certain images span multiple rows to create visual hierarchy and interest.
function ImageGallery() { const images = [ { id: 1, src: "https://images.unsplash.com/photo-1682686581660-3693f0c588d2", alt: "Mountain sunset", rowSpan: 3 }, { id: 2, src: "https://images.unsplash.com/photo-1682687220067-dced9a881b56", alt: "Ocean waves", rowSpan: 1 }, { id: 3, src: "https://images.unsplash.com/photo-1682686580224-cd46ea1a6950", alt: "Desert landscape", rowSpan: 1 }, { id: 4, src: "https://images.unsplash.com/photo-1682695796954-bad0d0f59ff1", alt: "Forest path", rowSpan: 1 }, { id: 5, src: "https://images.unsplash.com/photo-1682685796014-2f342188a635", alt: "Waterfall", rowSpan: 2 }, { id: 6, src: "https://images.unsplash.com/photo-1682695797221-8164ff1fafc9", alt: "City skyline", rowSpan: 1 } ]; return ( <div className="p-4"> <div className="grid grid-cols-3 gap-4"> {images.map((image) => ( <div key={image.id} className={`relative overflow-hidden rounded-lg shadow-lg ${image.rowSpan === 2 ? 'row-span-2' : ''} ${image.rowSpan === 3 ? 'row-span-3' : ''} `} > <img src={image.src} alt={image.alt} className={`w-full h-full object-cover transition-all duration-300 hover:brightness-125 hover:contrast-125 hover:saturate-150 ${image.rowSpan === 1 ? 'aspect-square' : ''} ${image.rowSpan === 2 ? 'aspect-[3/4]' : ''} ${image.rowSpan === 3 ? 'aspect-[3/5]' : ''} `} /> </div> ))} </div> </div> ); } export default ImageGallery;
Dashboard Stats
A metrics dashboard with varied row spans to show different levels of data detail and importance.
function ExtendedDashboard() { const metrics = [ { id: 1, title: "Revenue", value: "$54.2K", change: "+12%", trend: "up", rowSpan: 4, details: [ { label: "Last Month", value: "$48.5K" }, { label: "Target", value: "$60K" }, { label: "Q1 Average", value: "$52.3K" }, { label: "YoY Growth", value: "+15%" }, { label: "Monthly Recurring", value: "$42.8K" }, { label: "One-time Sales", value: "$11.4K" } ], chartData: [42, 45, 48, 52, 54, 58, 62] }, { id: 2, title: "Users", value: "2,845", change: "+5%", trend: "up", rowSpan: 3, details: [ { label: "Active", value: "2,142" }, { label: "New this month", value: "304" }, { label: "Churned", value: "86" } ] }, { id: 3, title: "Orders", value: "482", change: "+8%", trend: "up" }, { id: 4, title: "Conversion", value: "3.2%", change: "-1%", trend: "down", rowSpan: 5, details: [ { label: "Last Week", value: "3.4%" }, { label: "Best Day", value: "4.1%" }, { label: "Worst Day", value: "2.8%" }, { label: "Industry Avg", value: "2.9%" }, { label: "Target", value: "3.5%" }, { label: "Mobile", value: "2.8%" }, { label: "Desktop", value: "3.6%" } ] }, { id: 5, title: "Bounce Rate", value: "28.5%", change: "-2%", trend: "up", rowSpan: 2, details: [ { label: "Mobile", value: "32%" }, { label: "Desktop", value: "24%" } ] }, { id: 6, title: "Avg Time", value: "4m 32s", change: "+30s", trend: "up" } ]; return ( <div className="p-3 bg-gray-50"> <div className="grid grid-cols-2 gap-3"> {metrics.map((metric) => ( <div key={metric.id} className={`bg-white rounded-lg p-3 ${metric.rowSpan === 2 ? 'row-span-2' : ''} ${metric.rowSpan === 3 ? 'row-span-3' : ''} ${metric.rowSpan === 4 ? 'row-span-4' : ''} ${metric.rowSpan === 5 ? 'row-span-5' : ''} `} > <div className="flex justify-between items-start mb-2"> <span className="text-sm text-gray-600">{metric.title}</span> <span className={`text-xs px-1.5 py-0.5 rounded-full ${metric.trend === 'up' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}`} > {metric.change} </span> </div> <div className="text-xl font-semibold mb-2"> {metric.value} </div> {metric.details && ( <div className="mt-3 pt-3 border-t border-gray-100 space-y-2"> {metric.details.map((detail, idx) => ( <div key={idx} className="flex justify-between items-center"> <span className="text-xs text-gray-500">{detail.label}</span> <span className="text-xs font-medium">{detail.value}</span> </div> ))} </div> )} {metric.chartData && metric.rowSpan >= 4 && ( <div className="mt-4 h-24"> <div className="flex items-end justify-between h-full"> {metric.chartData.map((value, index) => ( <div key={index} className="w-full bg-blue-500 rounded-t mx-0.5" style={{ height: `${value}%` }} /> ))} </div> </div> )} </div> ))} </div> </div> ); } export default ExtendedDashboard;
Team Directory Extended
A team directory with varied row spans to show different levels of leadership and responsibility.
function TeamDirectoryExtended() { const team = [ { id: 1, name: "Sarah Chen", role: "Engineering Director", avatar: "https://images.unsplash.com/photo-1494790108377-be9c29b29330", rowSpan: 4, skills: ["Architecture", "Leadership", "Strategy"], stats: { teamSize: 24, projects: 8, completion: "92%" }, directReports: [ "Frontend Team (8)", "Backend Team (10)", "DevOps Team (6)" ], keyMetrics: [ { label: "Sprint Velocity", value: "85 pts" }, { label: "Code Quality", value: "A+" }, { label: "Team Satisfaction", value: "94%" } ], objectives: [ "Platform Scalability", "Team Growth", "Innovation" ] }, { id: 2, name: "Mike Ross", role: "Tech Lead", avatar: "https://images.unsplash.com/photo-1599566150163-29194dcaad36", rowSpan: 2, skills: ["React", "Node", "AWS"], stats: { teamSize: 8, projects: 4, completion: "88%" }, keyMetrics: [ { label: "Code Reviews", value: "24/week" }, { label: "PR Merge Rate", value: "92%" }, { label: "Build Success", value: "97%" } ], objectives: [ "Code Quality", "Team Mentoring", "Tech Innovation" ] }, { id: 3, name: "Lisa Wong", role: "Product Lead", avatar: "https://images.unsplash.com/photo-1573497019940-1c28c88b4f3e", rowSpan: 2, skills: ["Product Strategy", "UX", "Analytics"], stats: { features: 12, launches: 4, satisfaction: "88%" }, keyMetrics: [ { label: "Feature Adoption", value: "76%" }, { label: "User Growth", value: "+22%" }, { label: "NPS Score", value: "72" } ], objectives: [ "Market Expansion", "User Retention", "Product Innovation" ] }, { id: 4, name: "James Kim", role: "Senior Designer", avatar: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d", rowSpan: 4, skills: ["UI/UX", "Design Systems", "Research"], stats: { designs: 45, projects: 6, satisfaction: "91%" }, keyMetrics: [ { label: "Design Systems", value: "3 launched" }, { label: "User Testing", value: "86% positive" }, { label: "Design Iterations", value: "4.2 avg" } ], objectives: [ "Design System", "User Experience", "Brand Cohesion" ] }, { id: 5, name: "Emily Brown", role: "QA Lead", avatar: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80", rowSpan: 2, skills: ["Testing", "Automation", "CI/CD"], stats: { testCases: 850, automation: "75%", coverage: "92%" }, keyMetrics: [ { label: "Bug Detection", value: "94%" }, { label: "Test Coverage", value: "88%" }, { label: "Release Quality", value: "A" } ] }, { id: 6, name: "David Lee", role: "Backend Lead", avatar: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e", rowSpan: 2, skills: ["Python", "Microservices", "DevOps"], stats: { services: 16, uptime: "99.9%", performance: "94%" }, keyMetrics: [ { label: "API Response", value: "120ms avg" }, { label: "System Uptime", value: "99.9%" }, { label: "Code Coverage", value: "92%" } ], objectives: [ "System Reliability", "Performance", "Scalability" ] } ]; return ( <div className="p-3 bg-gray-50"> <div className="grid grid-cols-2 gap-3"> {team.map((member) => ( <div key={member.id} className={`bg-white rounded-lg p-3 ${member.rowSpan === 2 ? 'row-span-2' : ''} ${member.rowSpan === 3 ? 'row-span-3' : ''} ${member.rowSpan === 4 ? 'row-span-4' : ''} ${member.rowSpan === 5 ? 'row-span-5' : ''} `} > <div className="flex items-center space-x-3"> <img src={member.avatar} alt={member.name} className="w-10 h-10 rounded-full object-cover" /> <div> <h3 className="font-medium text-sm leading-tight"> {member.name} </h3> <p className="text-xs text-gray-600"> {member.role} </p> </div> </div> <div className="mt-3 pt-3 border-t border-gray-100"> <div className="flex flex-wrap gap-1 mb-2"> {member.skills.map((skill) => ( <span key={skill} className="px-2 py-0.5 bg-blue-50 text-blue-700 rounded text-xs" > {skill} </span> ))} </div> </div> {member.stats && ( <div className="mt-3 pt-3 border-t border-gray-100"> {Object.entries(member.stats).map(([key, value]) => ( <div key={key} className="flex justify-between items-center mb-1"> <span className="text-xs text-gray-500 capitalize"> {key.replace(/([A-Z])/g, ' $1').trim()} </span> <span className="text-xs font-medium">{value}</span> </div> ))} </div> )} {member.keyMetrics && member.rowSpan >= 3 && ( <div className="mt-3 pt-3 border-t border-gray-100"> <span className="text-xs font-medium text-gray-700">Key Metrics:</span> <div className="mt-2 space-y-1"> {member.keyMetrics.map((metric, idx) => ( <div key={idx} className="flex justify-between items-center"> <span className="text-xs text-gray-500">{metric.label}</span> <span className="text-xs font-medium">{metric.value}</span> </div> ))} </div> </div> )} {member.objectives && member.rowSpan >= 4 && ( <div className="mt-3 pt-3 border-t border-gray-100"> <span className="text-xs font-medium text-gray-700">Objectives:</span> <div className="mt-2 space-y-1"> {member.objectives.map((objective, idx) => ( <div key={idx} className="flex items-center"> <div className="w-1.5 h-1.5 rounded-full bg-blue-500 mr-2"></div> <span className="text-xs">{objective}</span> </div> ))} </div> </div> )} </div> ))} </div> </div> ); } export default TeamDirectoryExtended;
Customization Examples
Photo Gallery Grid
A compact photo gallery with featured images spanning multiple rows.
import tailwindConfig from "./tailwind.config.js"; tailwind.config = tailwindConfig; const PhotoGallery = () => { return ( <div className="w-full overflow-auto snap-y snap-mandatory"> <div className="grid grid-cols-2 gap-2 p-2"> <img src="https://images.unsplash.com/photo-1571690584414-fa2a1127a2e1" className="w-full object-cover rounded-lg row-span-16 snap-start" alt="Ocean Waves" /> <img src="https://images.unsplash.com/photo-1509743612824-2904ea16052b" className="w-full object-cover rounded-lg row-start-14 row-end-20 snap-start" alt="Mountain view" /> </div> </div> ) } export default PhotoGallery;
Dashboard Stats
A minimal dashboard with widgets using custom row spans.
import tailwindConfig from "./tailwind.config.js"; tailwind.config = tailwindConfig; const DashboardStats = () => { return ( <div className="w-full h-screen bg-gray-100 overflow-auto snap-y snap-mandatory"> <div className="grid grid-cols-2 gap-2 p-2"> <div className="bg-white rounded-lg shadow p-3 row-span-15 snap-start"> <h3 className="text-sm font-bold">Revenue</h3> <div className="mt-2 h-20 bg-gray-50 rounded"></div> </div> <div className="bg-white rounded-lg shadow p-3 row-start-13 row-end-18 snap-start"> <h3 className="text-sm font-bold">Users</h3> <div className="mt-2 h-16 bg-gray-50 rounded"></div> </div> <div className="bg-white rounded-lg shadow p-3 row-start-7 row-end-12 snap-start"> <h3 className="text-sm font-bold">Orders</h3> <div className="mt-2 h-16 bg-gray-50 rounded"></div> </div> <div className="bg-white rounded-lg shadow p-3 row-start-1 row-end-6 snap-start"> <h3 className="text-sm font-bold">Conversion Rate</h3> <div className="mt-2 h-16 bg-gray-50 rounded"></div> </div> </div> </div> ) } export default DashboardStats;
News Cards
A condensed news feed with articles in custom row positions.
import tailwindConfig from "./tailwind.config.js"; tailwind.config = tailwindConfig; const NewsCards = () => { const mainStory = { title: "Breakthrough in Renewable Energy Storage", image: "https://images.unsplash.com/photo-1509391366360-2e959784a276", alt: "Solar panel array in desert landscape", text: "Scientists have developed a revolutionary new battery technology that could solve one of renewable energy's biggest challenges: efficient, long-term storage.", author: "Sarah Chen", date: "Feb 4, 2024" }; const sideStory = { title: "Urban Farming Takes Root", image: "https://images.unsplash.com/photo-1530836369250-ef72a3f5cda8", alt: "Rooftop garden with city skyline", text: "Community gardens and rooftop farms are transforming urban spaces across major cities, providing fresh produce and green spaces in concrete jungles.", author: "Marcus Rodriguez", date: "Feb 4, 2024" }; return ( <div className="w-full h-screen overflow-auto snap-y snap-mandatory"> <div className="grid grid-cols-2 gap-2 p-2"> <article className="row-span-18 bg-white rounded-lg shadow snap-start"> <img src={mainStory.image} className="w-full h-48 object-cover rounded-t-lg" alt={mainStory.alt} /> <div className="p-4"> <h3 className="text-xl font-bold mb-3">{mainStory.title}</h3> <p className="text-gray-600 mb-4 leading-relaxed"> {mainStory.text} </p> <div className="flex items-center text-sm text-gray-500"> <span className="font-medium">{mainStory.author}</span> <span className="mx-2">•</span> <span>{mainStory.date}</span> </div> </div> </article> <article className="row-start-15 row-end-22 bg-white rounded-lg shadow snap-start"> <img src={sideStory.image} className="w-full h-32 object-cover rounded-t-lg" alt={sideStory.alt} /> <div className="p-3"> <h3 className="text-lg font-bold mb-2">{sideStory.title}</h3> <p className="text-gray-600 text-sm mb-3 leading-relaxed"> {sideStory.text} </p> <div className="flex items-center text-xs text-gray-500"> <span className="font-medium">{sideStory.author}</span> <span className="mx-2">•</span> <span>{sideStory.date}</span> </div> </div> </article> </div> </div> ) } export default NewsCards;
Best Practices
Maintain Design Consistency
Maintaining consistency in your project is essential when applying row-span-*
, row-start-*
, and row-end-*
utilities. One effective method is defining a standard grid template and sticking to predetermined row spans to align content predictably. Overuse of irregular spans can introduce inconsistency, making the layout harder to manage.
Use Tailwind’s predefined spacing and alignment utilities to further reinforce a consistent look and feel. When structuring your design, consider keeping uniformity in row spans and ensuring elements within a grid share similar spacing and alignment.
Leverage Utility Combinations
Combining multiple Tailwind utilities allows you to build powerful grid-based layouts without unnecessary complexity. For instance, pairing row-span-*
with col-span-*
enables dynamic content positioning that adheres to a structured framework. When used thoughtfully, these combinations can streamline content presentation, making interfaces more readable and functional.
For better clarity, consider integrating utilities such as gap-*
to control spacing between rows efficiently. Using justify-items-*
or align-items-*
can fine-tune how content is aligned within each grid row, ensuring that the layout remains visually balanced. Additionally, pairing row-start-*
and row-end-*
with col-start-*
and col-end-*
helps create a seamless content flow that aligns well with design expectations.
Accessibility Considerations
Enhance Readability and Navigability
Maintaining predictable row structures is key to keeping readability intact. By defining consistent row spans and ensuring elements align properly within their designated spaces, content remains easy to digest. Spacing between rows also plays an important role—using Tailwind’s gap-*
utilities alongside row-span-*
can help create distinct separations that visually segment content without overwhelming the user.
Additionally, ensuring an intuitive reading flow from one row to the next helps users quickly locate and interpret information without unnecessary scanning.
Focus on High Contrast
Maintaining adequate contrast between row-separated elements enhances content visibility and prevents information from blending into the background. Incorporating border-*
and gap-*
utilities can further emphasize separation between content blocks, making them easier to distinguish and navigate.
A useful approach is to apply bg-*
and text-*
utilities alongside row-span-*
to create clear distinctions between content sections. Additionally, using border-*
utilities allows you to create subtle visual dividers that segment different elements while maintaining design harmony.
For better accessibility, it’s also important to evaluate how grid row configurations affect user interaction. Using accessible color schemes, background contrasts, and well-placed borders allows users with visual impairments to distinguish content without unnecessary strain.