Menu

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.

ClassPropertiesExample
growflex-grow: 1;<div className="grow"></div>
grow-0flex-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:

This is a live editor. Play around with it!
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.

This is a live editor. Play around with it!
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:

This is a live editor. Play around with it!
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:

This is a live editor. Play around with it!
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:

This is a live editor. Play around with it!
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.

This is a live editor. Play around with it!
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.

This is a live editor. Play around with it!
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;

A search box where input will collapse to its default width without grow, making it much smaller than intended and breaking the layout.

This is a live editor. Play around with it!
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.

This is a live editor. Play around with it!
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.

This is a live editor. Play around with it!
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.

This is a live editor. Play around with it!
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.

This is a live editor. Play around with it!
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.

This is a live editor. Play around with it!
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;

A horizontal carousel where the active slide grows to showcase featured content.

This is a live editor. Play around with it!
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.