Tailwind CSS Flex Grow
Flex Grow defines how a flex item can expand when there is extra space available in its container. It helps you manage how content resizes to fill remaining space in a flex parent, ensuring cleaner, more adaptive layouts.
Tailwind CSS offers grow
and grow-0
utility classes to quickly enable or disable growth for specific items. In this guide, we will learn how to effectively work with the grow
utilities.
Class | Properties | Example |
---|---|---|
grow | flex-grow: 1; | <div className="grow"></div> |
grow-0 | flex-grow: 0; | <div className="grow-0"></div> |
Overview of Flex Grow
Adding the Flex Grow
Flex Grow expands an element to consume free space within the layout. In CSS, this is done by setting flex-grow: 1;
. In Tailwind, you use the utility grow
to grow the item in the container.
In the below example, the second item expands to fill the remaining space of the container due to the grow
utility:
export default function FlexGrow() { return ( <div className="flex gap-4 justify-between h-screen w-screen"> <div className="h-16 w-16 bg-blue-500"></div> {/* grow => flex-grow: 1; */} <div className="grow h-16 w-16 bg-green-500"></div> <div className="h-16 w-16 bg-red-500"></div> </div> ); }
Removing the Flex Grow
Controlling whether an item should not grow at all is useful when you want a defined size to remain fixed. In CSS, that corresponds to flex-grow: 0;
. To replicate this in Tailwind, use the grow-0
utility. This approach ensures your elements remain at their natural or defined width, even if there is space left in the container.
In the below example, the grow-0
targets the first two items to remain at its computed width. The third item with grow
then absorbs the leftover space.
export default function RemovingFlexGrow() { return ( <div className="flex gap-4 justify-between h-screen w-screen"> {/* grow-0 => flex-grow: 0; */} <div className="grow-0 h-16 w-16 bg-blue-500"></div> <div className="grow-0 h-16 w-16 bg-green-500"></div> <div className="grow h-16 w-16 bg-red-500"></div> </div> ); }
States and Responsiveness
When building sophisticated interfaces, you may want elements to grow only in certain states (like hover or focus) or at specific breakpoints. Tailwind’s state and responsive modifiers allow you to switch growth behavior on these conditions seamlessly.
Hover and Focus States
To modify growth in interactive states, Tailwind provides the hover:
or focus:
prefixes. Using grow
or grow-0
utilities only on hover or focus, you can create dynamic transitions where items expand or stay fixed based on user interaction.
In the below example, hover on an item to grow its size to fill the remaining space of the container:
export default function InteractiveGrowth() { return ( <div className="flex gap-4 justify-between h-screen w-screen"> {/* On hover => flex-grow: 1; */} <div className="hover:grow h-16 w-16 bg-blue-500"></div> <div className="hover:grow h-16 w-16 bg-green-500"></div> <div className="hover:grow h-16 w-16 bg-red-500"></div> </div> ); }
Breakpoint Modifiers
While designing responsive layouts, you might want an element to grow only beyond certain screen sizes or remain fixed at certain breakpoints. Tailwind’s breakpoint prefixes (such as sm:
, md:
, lg:
, and so forth) allow you to conditionally apply grow
or grow-0
.
In the below example, the first element grows on sm
breakpoint and above:
export default function ResponsiveGrowth() { return ( <div className="flex gap-4 justify-between h-screen w-screen"> {/* grow-0 => flex-grow: 0; at the default (small) sm:grow => flex-grow: 1; from sm breakpoint and up */} <div className="grow-0 sm:grow h-16 w-16 bg-blue-500"></div> <div className="h-16 w-16 bg-green-500"></div> <div className="h-16 w-16 bg-red-500"></div> </div> ); }
Custom Flex Grow
Tailwind’s default grow classes generally address most layouts you might create. However, you can also incorporate custom values using Tailwind’s configuration file or even apply one-off arbitrary values to handle unique scenarios. Adopting custom growth values can be a powerful way to refine how your UI adapts in more specialized designs.
Extending the Theme
To rotate beyond the basic 0
or 1
values for growth, Tailwind gives you the flexibility to add new flexGrow
settings via your tailwind.config.js
. This is especially beneficial if you consistently use certain numeric ratios across multiple elements or if you prefer more descriptive class names to standard numeric expansions.
After defining these values, you can then apply them directly in your project:
import tailwindConfig from "./tailwind.config.js"; tailwind.config = tailwindConfig; export default function ConfigGrow() { return ( <div className="flex gap-4 justify-between h-screen w-screen"> {/* grow-2 => flex-grow: 2; */} <div className="grow-2 h-16 w-16 bg-blue-500"></div> <div className="h-16 w-16 bg-green-500"></div> <div className="h-16 w-16 bg-red-500"></div> </div> ); }
Using Arbitrary Values
At times, you might need a quick adjustment that doesn’t exist in your theme. Rather than looking into the global tailwind.config.js
, one can leverage arbitrary values in Tailwind by specifying them within square brackets.
export default function TailoredGrowth() { return ( <div className="flex gap-4 justify-between h-screen w-screen"> {/* grow-[.5] => flex-grow: .5; */} <div className="grow-[.5] h-16 w-16 bg-blue-500"></div> <div className="h-16 w-16 bg-green-500"></div> <div className="h-16 w-16 bg-red-500"></div> </div> ); }
Real World Examples
Music Player Interface
A music player where the track progress bar grows to fill the space between controls.
export const MusicPlayer = () => { const playlist = [ { id: 1, title: "Midnight Dreams", artist: "Luna Eclipse", duration: "4:32", cover: "https://images.unsplash.com/photo-1493225457124-a3eb161ffa5f", alt: "Abstract purple waves" }, { id: 2, title: "Summer Breeze", artist: "The Wanderers", duration: "3:45", cover: "https://images.unsplash.com/photo-1459749411175-04bf5292ceea", alt: "Beach sunset" }, { id: 3, title: "Urban Rhythm", artist: "City Lights", duration: "5:18", cover: "https://images.unsplash.com/photo-1511671782779-c97d3d27a1d4", alt: "City lights at night" }, { id: 4, title: "Mountain High", artist: "Nature's Call", duration: "4:15", cover: "https://images.unsplash.com/photo-1454496522488-7a8e488e8606", alt: "Mountain peaks" }, { id: 5, title: "Electronic Dreams", artist: "Digital Wave", duration: "6:02", cover: "https://images.unsplash.com/photo-1614149162883-504ce4d13909", alt: "Abstract digital art" }, { id: 6, title: "Acoustic Journey", artist: "Wood & Strings", duration: "3:56", cover: "https://images.unsplash.com/photo-1511379938547-c1f69419868d", alt: "Acoustic guitar" } ]; return ( <div className="h-screen bg-gray-900 text-white p-6"> <div className="h-full flex flex-col"> <div className="mb-6"> <h3 className="text-xl font-bold mb-4">Now Playing</h3> <img src={playlist[0].cover} alt={playlist[0].alt} className="w-32 h-32 rounded-lg shadow-lg" /> </div> <div className="grow overflow-y-auto mb-4"> {playlist.map((track) => ( <div key={track.id} className="flex items-center p-2 hover:bg-gray-800 rounded"> <img src={track.cover} alt={track.alt} className="w-12 h-12 rounded" /> <div className="ml-3 grow"> <div className="font-medium">{track.title}</div> <div className="text-gray-400 text-sm">{track.artist}</div> </div> <div className="text-gray-400">{track.duration}</div> </div> ))} </div> <div className="flex items-center space-x-4"> <button className="p-2"> <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" /> </svg> </button> <div className="grow bg-gray-800 h-1 rounded-full"> <div className="w-1/3 h-full bg-white rounded-full"></div> </div> <button className="p-2"> <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" /> </svg> </button> </div> </div> </div> ); }; export default MusicPlayer;
SearchBar
A search box where input will collapse to its default width without grow
, making it much smaller than intended and breaking the layout.
import React from 'react'; // Search Bar with Filters const SearchBar = () => { const data = { recentSearches: [ 'Wireless headphones', 'Gaming laptop', 'Mechanical keyboard', 'Ultra-wide monitor', 'Ergonomic chair', 'Standing desk' ] }; return ( // Container with fixed width to demonstrate grow effect <div className="w-[350px] p-4 bg-gray-50"> {/* Flex container with full width */} <div className="w-full flex items-center gap-2 mb-4"> {/* Non-growing button with fixed width */} <button className="flex-none w-20 px-2 py-2 bg-white border rounded-lg shadow-sm"> 🔍 Filters </button> {/* Input that grows - remove grow to see it shrink */} <input type="text" className="grow w-28 min-w-0 px-4 py-2 bg-white border rounded-lg shadow-sm" placeholder="Search products..." /> {/* Non-growing button with fixed width */} <button className="flex-none w-20 px-2 py-2 bg-blue-500 text-white rounded-lg shadow-sm"> Search </button> </div> <div className="text-sm text-gray-500 flex gap-2 flex-wrap"> Recent: {data.recentSearches.map((search, index) => ( <button key={index} className="px-3 py-1 bg-white border rounded-full text-xs hover:bg-gray-50" > {search} </button> ))} </div> </div> ); }; export default SearchBar;
File Upload Progress
A file upload interface where the filename grows on hover.
const FileUpload = () => { const files = [ { id: 1, name: 'Project_Presentation.pdf', progress: 100 }, { id: 2, name: 'Design_Assets_Final.zip', progress: 75 }, { id: 3, name: 'Team_Photo_Final.jpg', progress: 45 }, { id: 4, name: 'Meeting_Notes_Final.pdf', progress: 20 } ]; return ( <div className="w-[350px] p-4 bg-gray-50"> {files.map(file => ( <div key={file.id} className="w-full flex items-center gap-2 mb-3 bg-white p-3 rounded-lg group"> <div className="flex-none w-8 h-8 bg-blue-100 rounded-lg flex items-center justify-center text-blue-500"> 📄 </div> <span className="group-hover:grow w-32 min-w-0 truncate"> {file.name} </span> <div className="flex-none w-16 text-sm text-gray-500"> {file.progress}% </div> </div> ))} <button className="w-full mt-2 py-2 bg-blue-500 text-white rounded-lg"> Upload More </button> </div> ); }; export default FileUpload;
Stat Card With Icon
A stat card where the text area grows while keeping the icon fixed.
const StatCard = () => { const stats = [ { id: 1, icon: "📈", label: "Revenue Growth", value: "+24.5%", change: "+2.1%", description: "Compared to last month" }, { id: 2, icon: "👥", label: "Active Users", value: "12,345", change: "+5.3%", description: "New users this week" }, { id: 3, icon: "🎯", label: "Conversion Rate", value: "3.2%", change: "+0.8%", description: "Above target goal" } ]; return ( <div className="w-[350px] p-4 bg-gray-50"> {stats.map(stat => ( <div key={stat.id} className="mb-3 bg-white p-4 rounded-lg shadow-sm"> <div className="w-full flex items-center gap-4"> <div className="flex-none w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center text-2xl"> {stat.icon} </div> <div className="grow min-w-0 w-40"> <div className="text-sm text-gray-600">{stat.label}</div> <div className="text-2xl font-semibold">{stat.value}</div> <div className="flex items-center text-sm"> <span className="text-green-600">{stat.change}</span> <span className="text-gray-500 ml-2">{stat.description}</span> </div> </div> </div> </div> ))} </div> ); }; export default StatCard;
Action Card With Badge
A card that use grow
to for proper alignment and spacing between the elements.
const ActionCard = () => { const cards = [ { id: 1, title: "Website Redesign", badge: "In Progress", description: "Complete homepage mockup", dueDate: "Due Tomorrow", progress: 65 }, { id: 2, title: "Mobile App Update", badge: "Review", description: "Fix navigation bugs", dueDate: "Due Today", progress: 80 }, { id: 3, title: "User Testing", badge: "Planned", description: "Prepare test scenarios", dueDate: "Next Week", progress: 20 } ]; return ( <div className="w-[350px] p-4 bg-gray-50"> {cards.map(card => ( <div key={card.id} className="mb-3 bg-white p-4 rounded-lg shadow-sm"> <div className="w-full flex items-start gap-3"> <div className="grow w-40"> <h3 className="font-semibold text-lg truncate">{card.title}</h3> <p className=" text-gray-600 text-sm">{card.description}</p> <div className=" mt-2 text-sm text-gray-500">{card.dueDate}</div> </div> <div className="flex-none"> <span className=" px-2 py-1 text-xs bg-blue-100 text-blue-700 rounded-full"> {card.badge} </span> </div> </div> <div className=" mt-3 w-full bg-gray-200 rounded-full h-2"> <div className=" bg-blue-500 rounded-full h-2" style={{ width: `${card.progress}%` }} /> </div> </div> ))} </div> ); }; export default ActionCard;
Customization Examples
Feature List
A vertical list of features that expand to reveal more content when clicked.
import tailwindConfig from "./tailwind.config.js"; tailwind.config = tailwindConfig; import React, { useState } from 'react'; const FeatureList = () => { const [activeFeature, setActiveFeature] = useState(null); const features = [ { title: 'Cloud Storage', description: 'Secure and scalable cloud storage solutions for your business', color: 'bg-indigo-500' }, { title: 'Analytics', description: 'Advanced analytics and reporting tools', color: 'bg-pink-500' }, { title: 'Security', description: 'Enterprise-grade security features and compliance', color: 'bg-amber-500' } ]; return ( <div className="flex flex-col gap-2 p-4 h-96"> {features.map((feature, index) => ( <div key={index} onClick={() => setActiveFeature(index)} className={`${feature.color} rounded-lg text-white p-4 transition-all duration-300 cursor-pointer ${activeFeature === index ? 'grow-12' : 'grow'}`} > <h3 className="text-xl font-bold">{feature.title}</h3> {activeFeature === index && ( <p className="mt-2">{feature.description}</p> )} </div> ))} </div> ); }; export default FeatureList;
Image Section
A vertical section of expandable image that grow to reveal further details.
import tailwindConfig from "./tailwind.config.js"; tailwind.config = tailwindConfig; import { useState } from 'react'; const DynamicSidebar = () => { const [activeSection, setActiveSection] = useState(null); const sections = [ { id: 'nature', title: 'Nature Escapes', image: 'https://images.unsplash.com/photo-1472214103451-9374bd1c798e', description: 'Discover serene landscapes' }, { id: 'urban', title: 'Urban Life', image: 'https://images.unsplash.com/photo-1449824913935-59a10b8d2000', description: 'Experience city vibrancy' }, { id: 'adventure', title: 'Adventure Sports', image: 'https://images.unsplash.com/photo-1519904981063-b0cf448d479e', description: 'Push your limits' } ]; return ( <div className="flex flex-col h-screen "> {sections.map(section => ( <div key={section.id} onClick={() => setActiveSection(section.id)} className={`relative transition-all duration-300 cursor-pointer ${activeSection === section.id ? 'grow-8' : 'grow'}`} > <img src={section.image} alt={section.title} className="absolute inset-0 w-full h-full object-cover" /> <div className="absolute inset-0 bg-black/40 hover:bg-black/60 transition-colors"> <div className="h-full flex flex-col justify-center p-4 text-white"> <h3 className="text-xl font-bold">{section.title}</h3> {activeSection === section.id && ( <p className="mt-2 text-sm">{section.description}</p> )} </div> </div> </div> ))} </div> ); }; export default DynamicSidebar;
Content Carousel
A horizontal carousel where the active slide grows to showcase featured content.
import tailwindConfig from "./tailwind.config.js"; tailwind.config = tailwindConfig; import { useState } from 'react'; const ContentCarousel = () => { const [activeSlide, setActiveSlide] = useState(0); const slides = [ { image: 'https://images.unsplash.com/photo-1551632811-561732d1e306', title: 'Morning Brewing', subtitle: 'Perfect start to your day' }, { image: 'https://images.unsplash.com/photo-1495474472287-4d71bcdd2085', title: 'Afternoon Tea', subtitle: 'A moment of calm' }, { image: 'https://images.unsplash.com/photo-1497935586351-b67a49e012bf', title: 'Evening Blends', subtitle: 'Wind down in style' } ]; return ( <div className="flex h-96 gap-1 p-2"> {slides.map((slide, index) => ( <div key={index} onClick={() => setActiveSlide(index)} className={`relative rounded-lg overflow-hidden transition-all duration-500 ${activeSlide === index ? 'grow-15' : 'grow-2'}`} > <img src={slide.image} alt={slide.title} className="absolute inset-0 h-full w-full object-cover" /> <div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/20 to-transparent"> <div className="absolute bottom-0 p-4 text-white"> <h3 className="text-xl font-bold">{slide.title}</h3> {activeSlide === index && ( <p className="mt-2 text-sm opacity-90">{slide.subtitle}</p> )} </div> </div> </div> ))} </div> ); }; export default ContentCarousel;
Best Practices
Optimize for Reusability
Reusable components ensure rapid development and consistency. A well-structured reusable card, for instance, might have grow
on an image section and grow-0
for textual content. If these cards appear in multiple contexts, their uniform application reduces duplicated code. By toggling certain props or states, you can easily adjust their layout expansions.
Maintaining reusability may involve abstracting unique styling into dedicated utility classes or using your tailwind.config.js
. Doing so allows you to quickly update the entire codebase if the design language evolves.
Build Responsive Design
Responsive design is fundamental to modern web development, and Tailwind offers an efficient pathway through flex-based layouts. You can combine sm:grow
, md:grow-0
, etc., or other breakpoint modifiers to swap between flexible or fixed widths. This method ensures that, on smaller screens, items can remain fixed if necessary, but smoothly expand to fill space on tablets or desktops.
When building fully responsive layouts, test across multiple devices. Thorough testing fosters a fluid experience that aligns with a wide range of aspect ratios and form factors, from smartphones to larger laptops.
Accessibility Considerations
Enhance Readability and Navigability
Flex Grow can help structure content so that headings, paragraphs, and imagery remain visually grouped. A strategic approach to growth means key information is not squeezed into tiny columns or overshadowed by neighboring sections. When content is easier to parse, users with screen readers or cognitive impairments can follow the structure more effectively.
Properly labeling sections also aids navigability. Using accessible tags and roles, in coordination with flex-based expansions, clarifies the hierarchy to assistive technologies.
Focus on High Contrast
While flex properties deal with space distribution, it is vital to pair them with accessible color choices and sufficient contrast. When an element grows, it might occupy more real estate, making it a focal point. A high-contrast palette directs attention to the most critical content, aiding those with visual impairments or color-vision deficiencies.
Ensure that expanded elements featuring text overlays use background classes and text colors that exceed the minimal contrast ratios recommended by accessibility guidelines. Leveraging Tailwind’s built-in color utilities can help you systematically enforce these ratios. If you have custom brand colors, verify their contrast levels for both normal and larger text.
Debugging Common Issues
Iterative Testing and Maintenance
Frequent testing at the component level is vital to catching layout issues early. Whenever you add or change growth utilities, confirm that each impacted section still displays correctly across different screen sizes. Maintaining a simple checklist for layout conformance can prevent painful debugging sessions later.
Pair these iterations with version control for a stable workflow. Commit changes as soon as a layout is successfully tested. Should a regression appear, rolling back to a previous commit is more reliable than manually undoing multiple changes. This process also forms a clear history of what might have created a layout instability.
Leverage Browser DevTools
Most modern browsers provide inspection tools that quickly highlight flex item behavior. By using the Layout panel, you can verify which items are growing and how much space they consume. This direct feedback helps you spot unexpected distribution patterns or confirm that a growth utility is active.
For persistent visual gatherings or misalignments, experiment with toggling growth properties off and on in the browser. This approach, done in real-time, can guide you toward a more suitable combination of utilities. Document any necessary changes so the final code matches what you verified during live testing, ensuring consistent, harmonious layouts.