Tailwind CSS Flex
The Flex property is a shorthand for flex-grow
, flex-shrink
, and flex-basis
. Tailwind offers a set of predefined utilities- flex-1
, flex-auto
, flex-initial
, and flex-none
to use the flex property in your code.
In this guide, we will explore these utilities in detail, covering default setups, state-based changes, responsive breakpoints, theme extensions, and more.
Class | Properties | Example |
---|---|---|
flex-1 | flex: 1 1 0%; | <div className="flex-1"></div> |
flex-auto | flex: 1 1 auto; | <div className="flex-auto"></div> |
flex-initial | flex: 0 1 auto; | <div className="flex-initial"></div> |
flex-none | flex: none; | <div className="flex-none"></div> |
Overview of Flex
Adding the flex-initial
The flex-initial
class in Tailwind CSS sets a flex item to use the default behavior of the flex shorthand property: 0 1 auto
. This means the item does not grow (flex-grow: 0
), shrinks as needed to fit the container (flex-shrink: 1
), and the item's size is based on its content or the width/height properties if explicitly set. This utility is ideal for items that should maintain their intrinsic size unless the container's space is limited.
export default function FlexInitial() { return ( <div className="h-screen w-screen bg-gray-100 flex justify-center gap-4"> {/* flex-initial sets the default behavior of "0 1 auto" to the flex item */} <div className="flex-initial bg-blue-200 flex items-center justify-center text-center"> <p className="text-gray-700">First Section (flex-initial)</p> </div> <div className="flex-initial bg-blue-400 flex items-center justify-center text-center"> <p className="text-gray-700">Second Section (flex-initial)</p> </div> </div> ); }
Adding the flex-1
The flex-1
class in Tailwind CSS sets a flex item to grow and fill the available space proportionally with other flex-1
items in the same container. It applies the shorthand 1 1 0%
, meaning the item can grow (flex-grow: 1
), shrink if necessary (flex-shrink: 1
), and starts with a base size of 0%
. This utility is perfect for creating layouts where items need to divide the available space equally.
export default function GrowthFactorShowcase() { return ( <div className="h-screen w-screen bg-gray-100 flex"> <div className="flex-1 bg-blue-200 flex items-center justify-center"> {/* flex-1: can grow to fill the container */} <p className="text-gray-700">First Section (flex-1)</p> </div> <div className="flex-1 bg-blue-400 flex items-center justify-center"> <p className="text-gray-700">Second Section (flex-1)</p> </div> </div> ); }
Adding the flex-auto
The flex-auto
class in Tailwind CSS allows a flex item to grow and shrink as needed to fit the available space while using its content size as the starting point. It applies the shorthand 1 1 auto
, meaning the item will grow (flex-grow: 1
), shrink (flex-shrink: 1
), and its initial size is based on its content or explicit width/height values. This class is useful for items that need to adapt dynamically while respecting their content size.
export default function AutoFlexContainer() { return ( <div className="h-screen w-screen bg-gray-200 flex"> {/* This container has two children: - The first with flex-auto - The second with fixed width */} <div className="flex-auto bg-green-200 flex items-center justify-center text-center"> <p className="text-green-700">Content expands automatically here.</p> </div> <div className="w-1/4 bg-green-500 flex items-center justify-center text-center"> {/* This div retains a fixed fraction of the container width */} <p className="text-green-50">Fixed width area</p> </div> </div> ); }
Adding the flex-none
The flex-none
class prevents a flex item from growing or shrinking within a flex container. It applies the shorthand 0 0 auto
, meaning the item retains its original size based on its content or explicitly set width/height properties and ignores available or constrained space in the container. This utility is ideal for items that require a fixed size regardless of the container's dimensions.
export default function NoneFlexContainer() { return ( <div className="h-screen w-screen bg-gray-300 flex"> <div className="flex-none w-48 bg-purple-300 flex items-center justify-center text-center"> {/* flex-none: do not grow or shrink */} <p className="text-purple-900">Sidebar width is fixed.</p> </div> <div className="flex-1 bg-purple-100 flex items-center justify-center text-center"> <p className="text-purple-900">Main content resizes.</p> </div> </div> ); }
States and Responsiveness
Tailwind CSS makes it straightforward to conditionally apply styles based on user interactions and screen sizes. By leveraging state and responsive modifiers, you can build dynamic and adaptive layouts that respond to user behavior and device breakpoints. In this section, we’ll cover how to integrate these utilities with hover, focus, and other states, as well as how to leverage breakpoint modifiers for more refined, adaptive control.
Hover and Focus States
In many designs, you may want to alter how flex items behave when a user hovers over them or when an element is in focus. Tailwind's state variants (like hover:
, focus:
, etc.) can be prefixed to your flex utility classes, making it easy to toggle different growth or shrink behaviors.
export default function InteractiveFlex() { return ( <div className="h-screen w-screen bg-gray-100 flex gap-4 p-4"> {/* On hover, this element changes from flex-1 to flex-none, preventing it from growing and causing adjacent elements to expand. */} <div className="flex-1 hover:flex-none w-32 bg-pink-200 flex items-center justify-center text-center"> <p className="text-pink-800">Hover to change "flex-1" to "flex-none"</p> </div> <div className="flex-1 bg-pink-400 flex items-center justify-center text-center"> <p className="text-pink-900">Flexible sibling</p> </div> </div> ); }
Breakpoint Modifiers
Responsive design frequently dictates that certain layout choices differ across screen sizes. For example, you may want an element to remain flexible on larger screens but fixed on smaller ones. Tailwind CSS provides responsive modifiers (like sm:
, md:
, lg:
, etc.) to selectively apply flex classes at various breakpoints.
export default function ResponsiveFlexAdjust() { return ( <div className="h-screen w-screen bg-white flex flex-wrap"> <div className="flex-1 bg-yellow-200 flex items-center justify-center md:flex-none md:w-1/4 text-center"> {/* By default: flex-1 (remain flexible) On md breakpoint and above: flex-none with a 25% width */} <p className="text-yellow-800">Responsive item shrinks on larger viewports.</p> </div> <div className="flex-1 bg-yellow-400 flex items-center justify-center text-center"> <p className="text-yellow-900">The rest fills remaining space.</p> </div> </div> ); }
Custom Flex
While Tailwind’s core classes cover most scenarios, certain layouts require more granular control, whether that involves novel growth ratios or specialized spacing. Tailwind provides two main methods for customizing the flex utilities: extending the default theme and using arbitrary values. These techniques enable you to incorporate project-specific variables or apply experimental proportions that aren’t included in the standard utility set.
Extending the Theme
By extending the theme in your tailwind.config.js
, you can define custom flex classes that fit your specific design patterns, ensuring consistency and reusability.
After defining these in your configuration, you can reference them as classes such as flex-2
, flex-3
, or flex-half
directly in your components. Here's an example with custom flex
values:
import tailwindConfig from "./tailwind.config.js"; tailwind.config = tailwindConfig; export default function ExtendedThemeFlex() { return ( <div className="h-screen w-screen bg-gray-100 flex"> <div className="flex-2 bg-red-200 flex items-center justify-center"> {/* This section grows at twice the rate set in tailwind.config.js */} <p className="text-red-800">flex-2 in action</p> </div> <div className="flex-1 bg-red-400 flex items-center justify-center"> <p className="text-red-900">Default flex-1</p> </div> </div> ); }
Using Arbitrary Values
Tailwind’s arbitrary value feature lets you define classes inline, applying styles directly without any preceding configuration. When you use an arbitrary value for flex, you’re manually specifying the flex shorthand (grow, shrink, basis) in a single string.
For instance, to produce a growth factor of 1.5 while preserving a shrink factor of 1, you might do something like flex-[1.5_1_0%]
.
export default function ArbitraryFlexUsage() { return ( <div className="h-screen w-screen bg-gray-50 flex"> <div className="flex-[1.5_1_0%] bg-indigo-200 flex items-center justify-center"> {/* flex-[1.5_1_0%] => flex: 1.5 1 0%; Grows faster than default but still allows shrinking. */} <p className="text-indigo-800">Arbitrary flex ratio applied.</p> </div> <div className="flex-[0.5_1_0%] bg-indigo-400 flex items-center justify-center"> {/* flex-[0.5_1_0%] => flex: 0.5 1 0%; Slower growth compared to sibling, but can still shrink. */} <p className="text-indigo-900">Smaller base with shared space.</p> </div> </div> ); }
Real World Examples
Product Listing
A product listing section that uses flex-1
in product details to allow the name and price to expand and push the rating to the right.
const ProductComparison = () => { const products = [ { name: "Premium Headphones", price: "$299", rating: "4.8", reviews: "2,145", image: "https://images.unsplash.com/photo-1505740420928-5e560c06d30e", alt: "Premium wireless headphones" }, { name: "Wireless Earbuds", price: "$199", rating: "4.6", reviews: "1,823", image: "https://images.unsplash.com/photo-1572569511254-d8f925fe2cbb", alt: "Wireless earbuds in charging case" }, { name: "Studio Monitors", price: "$399", rating: "4.9", reviews: "978", image: "https://images.unsplash.com/photo-1558089687-f282ffcbc126", alt: "Professional studio monitor speakers" }, { name: "Noise Cancelling", price: "$349", rating: "4.7", reviews: "2,654", image: "https://images.unsplash.com/photo-1546435770-a3e426bf472b", alt: "Noise cancelling headphones" }, ]; return ( <div className="w-full max-w-sm mx-auto bg-white rounded-lg shadow-lg"> <h2 className="text-xl font-bold p-4 border-b">Compare Audio Devices</h2> <div className="flex flex-col divide-y"> {products.map((product) => ( <div key={product.name} className="flex items-center p-4"> <img src={product.image} alt={product.alt} className="w-16 h-16 object-cover rounded" /> <div className="flex-1 ml-4"> <h3 className="font-medium">{product.name}</h3> <p className="text-sm text-gray-500">{product.price}</p> </div> <div className="flex items-center"> <span className="text-yellow-500">★</span> <span className="ml-1 text-sm">{product.rating}</span> </div> </div> ))} </div> </div> ); }; export default ProductComparison;
Media Player
A media player that uses a combination of flex-initial
and flex-1
to create a structured interface.
const MediaPlayer = () => { const playlist = [ { title: "Summer Vibes", artist: "Groove Masters", duration: "3:45", cover: "https://images.unsplash.com/photo-1514525253161-7a46d19cd819", alt: "Summer vibes album cover" }, { title: "Midnight Blues", artist: "Jazz Ensemble", duration: "4:20", cover: "https://images.unsplash.com/photo-1511671782779-c97d3d27a1d4", alt: "Midnight blues album cover" }, { title: "Urban Beat", artist: "Street Rhythm", duration: "3:15", cover: "https://images.unsplash.com/photo-1504898770365-14faca6a7320", alt: "Urban beat album cover" }, { title: "Ocean Waves", artist: "Nature Sounds", duration: "5:30", cover: "https://images.unsplash.com/photo-1507525428034-b723cf961d3e", alt: "Ocean waves album cover" }, { title: "Mountain Echo", artist: "Ambient Collective", duration: "4:55", cover: "https://images.unsplash.com/photo-1519681393784-d120267933ba", alt: "Mountain echo album cover" }, { title: "City Lights", artist: "Electronic Duo", duration: "3:50", cover: "https://images.unsplash.com/photo-1515263487990-61b07816b324", alt: "City lights album cover" } ]; return ( <div className="w-full max-w-sm mx-auto bg-gray-900 rounded-lg shadow-lg text-white"> <div className="p-4 border-b border-gray-800"> <h2 className="text-xl font-bold">Now Playing</h2> </div> <div className="divide-y divide-gray-800"> {playlist.map((track, index) => ( <div key={track.title} className={`flex items-center p-4 ${ index === 0 ? "bg-gray-800" : "" }`} > <div className="flex-initial w-8 text-gray-500">{index + 1}</div> <img src={track.cover} alt={track.alt} className="w-12 h-12 object-cover rounded" /> <div className="flex-1 ml-4"> <h3 className="font-medium">{track.title}</h3> <p className="text-sm text-gray-400">{track.artist}</p> </div> <div className="flex-initial text-gray-500">{track.duration}</div> </div> ))} </div> </div> ); }; export default MediaPlayer;
Task Board
A scrollable Kanban-style board that uses flex-none
to create fixed-width columns.
export const TaskBoard = () => { const tasks = [ { id: 1, title: "Design System Updates", priority: "High", assignee: "Sarah Wilson", dueDate: "2024-02-01" }, { id: 2, title: "Bug Fixes for Mobile App", priority: "Medium", assignee: "John Davis", dueDate: "2024-02-03" }, { id: 3, title: "User Research", priority: "Low", assignee: "Emma Thompson", dueDate: "2024-02-05" } ]; return ( <div className="flex gap-4 overflow-x-auto p-4"> {["To Do", "In Progress", "Review", "Done"].map((column) => ( <div key={column} className="flex-none w-72 bg-gray-100 p-4 rounded-lg"> <h3 className="font-semibold mb-4">{column}</h3> <div className="space-y-3"> {tasks.map((task) => ( <div key={task.id} className="bg-white p-3 rounded shadow"> <h4 className="font-medium">{task.title}</h4> <div className="mt-2 text-sm text-gray-600"> <div>Assignee: {task.assignee}</div> <div>Due: {task.dueDate}</div> <span className="inline-block mt-1 px-2 py-1 bg-gray-100 rounded text-xs"> {task.priority} </span> </div> </div> ))} </div> </div> ))} </div> ); }; export default TaskBoard;
File Upload Progress
A file upload progress section that uses a combination of flex-initial
and flex-1
to create a structured interface with proper spacing and alignment.
const FileUploadProgress = () => { const files = [ { name: "project-presentation.pptx", size: "2.4 MB", progress: 100, type: "presentation" }, { name: "quarterly-report.pdf", size: "1.8 MB", progress: 75, type: "document" }, { name: "team-photo.jpg", size: "3.2 MB", progress: 45, type: "image" }, { name: "client-feedback.docx", size: "956 KB", progress: 90, type: "document" }, { name: "budget-2024.xlsx", size: "1.1 MB", progress: 60, type: "spreadsheet" }, { name: "product-demo.mp4", size: "28.4 MB", progress: 30, type: "video" } ]; return ( <div className="space-y-4"> {files.map((file, index) => ( <div key={index} className="flex items-center space-x-4 p-4 bg-gray-50 rounded-lg"> <div className="flex-initial w-8"> <svg className="w-6 h-6 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /> </svg> </div> <div className="flex-1"> <p className="font-medium">{file.name}</p> <p className="text-sm text-gray-500">{file.size}</p> </div> <div className="flex-1"> <div className="w-full bg-gray-200 rounded-full h-2"> <div className="bg-blue-600 rounded-full h-2" style={{ width: `${file.progress}%` }} /> </div> </div> <div className="flex-initial w-16 text-right"> <span className="text-sm font-medium">{file.progress}%</span> </div> </div> ))} </div> ); }; export default FileUploadProgress;
Messaging Interface
A chat app where flex-1
allows message content to fill available space while keeping time stamps aligned.
const MessagingInterface = () => { const messages = [ { sender: "Alice Cooper", avatar: "https://images.unsplash.com/photo-1494790108377-be9c29b29330", message: "Hey, how's the project coming along?", time: "9:30 AM", status: "read" }, { sender: "Bob Dylan", avatar: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e", message: "Just finished the design review. Looking good!", time: "10:15 AM", status: "sent" }, { sender: "Carol Smith", avatar: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80", message: "Can we schedule a meeting for tomorrow?", time: "11:00 AM", status: "delivered" }, { sender: "David Brown", avatar: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e", message: "The client loved our presentation!", time: "11:45 AM", status: "read" }, { sender: "Eve Johnson", avatar: "https://images.unsplash.com/photo-1544725176-7c40e5a71c5e", message: "New requirements just came in.", time: "12:30 PM", status: "sent" }, { sender: "Frank Wilson", avatar: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d", message: "Let's review the changes tomorrow morning.", time: "1:15 PM", status: "delivered" } ]; return ( <div className="max-w-md mx-auto bg-white"> {messages.map((msg, index) => ( <div key={index} className="flex items-center p-4 hover:bg-gray-50"> <img src={msg.avatar} alt={msg.sender} className="w-12 h-12 rounded-full" /> <div className="flex-1 min-w-0 ml-4"> <div className="flex items-baseline"> <h3 className="font-semibold truncate">{msg.sender}</h3> <span className="ml-2 text-sm text-gray-500">{msg.time}</span> </div> <p className="text-gray-600 truncate">{msg.message}</p> </div> <div className="ml-4"> {msg.status === "read" && ( <div className="w-4 h-4 rounded-full bg-blue-500" /> )} {msg.status === "delivered" && ( <div className="w-4 h-4 rounded-full bg-gray-500" /> )} {msg.status === "sent" && ( <div className="w-4 h-4 rounded-full bg-green-500" /> )} </div> </div> ))} </div> ); }; export default MessagingInterface;
Customization Examples
Product Grid
A responsive product grid layout that adapts to different screen sizes.
import tailwindConfig from "./tailwind.config.js"; tailwind.config = tailwindConfig; export default function ProductGrid() { const products = [ { id: 1, title: "Wireless Headphones", price: "$299", image: "https://images.unsplash.com/photo-1505740420928-5e560c06d30e" }, { id: 2, title: "Smart Watch", price: "$199", image: "https://images.unsplash.com/photo-1523275335684-37898b6baf30" }, ]; return ( <div className="container mx-auto p-8"> <div className="flex flex-wrap gap-6 justify-center"> {products.map((product) => ( <div key={product.id} className="flex-card bg-white rounded-lg shadow-lg overflow-hidden"> <img src={product.image} alt={product.title} className="w-full h-48 object-cover" /> <div className="p-4"> <h3 className="text-xl font-semibold">{product.title}</h3> <p className="text-gray-600">{product.price}</p> </div> </div> ))} </div> </div> ); }
Dynamic Photo Gallery
A responsive photo gallery where each image enlarges on click using custom flex values.
import tailwindConfig from "./tailwind.config.js"; tailwind.config = tailwindConfig; const PhotoGallery = () => { const photos = [ { id: 1, url: 'https://images.unsplash.com/photo-1682687220742-aba13b6e50ba' }, { id: 2, url: 'https://images.unsplash.com/photo-1682687220509-61b8a906ca19' }, { id: 3, url: 'https://images.unsplash.com/photo-1682687220198-88e9bdea9931' }, { id: 4, url: 'https://images.unsplash.com/photo-1564349683136-77e08dba1ef7' }, ]; return ( <div className="min-h-screen bg-gray-100 p-8"> <div className="flex flex-wrap gap-4"> {photos.map((photo) => ( <div tabindex="0" key={photo.id} className="flex-2 focus:flex-gallery transition-all duration-300 rounded-lg overflow-hidden shadow-lg" > <img src={photo.url} alt="Gallery item" className="w-full h-full object-cover cursor-pointer" /> </div> ))} </div> </div> ); }; export default PhotoGallery;
Dynamic Masonry Gallery
A responsive masonry gallery with hover effects that showcases images in dynamic columns.
import tailwindConfig from "./tailwind.config.js"; tailwind.config = tailwindConfig; // App.js export default function GalleryMasonry() { const images = [ "https://images.unsplash.com/photo-1661956602116-aa6865609028", "https://images.unsplash.com/photo-1564349683136-77e08dba1ef7", "https://images.unsplash.com/photo-1558979158-65a1eaa08691", "https://images.unsplash.com/photo-1682687220742-aba13b6e50ba", "https://images.unsplash.com/photo-1682687220198-88e9bdea9931", "https://images.unsplash.com/photo-1529419412599-7bb870e11810" ]; return ( <div className="container mx-auto p-8"> <div className="flex flex-wrap gap-4"> <div className="flex-gallery-col flex flex-col gap-4"> {images.slice(0, 3).map((image, index) => ( <div key={index} className="flex-gallery-item relative overflow-hidden rounded-lg" > <img src={image} alt={`Gallery image ${index + 1}`} className="w-full h-auto object-cover transition-transform hover:scale-105" /> </div> ))} </div> <div className="flex-gallery-col flex flex-col gap-4"> {images.slice(3, 6).map((image, index) => ( <div key={index} className="flex-gallery-item relative overflow-hidden rounded-lg" > <img src={image} alt={`Gallery image ${index + 4}`} className="w-full h-auto object-cover transition-transform hover:scale-105" /> </div> ))} </div> <div className="flex-gallery-col flex flex-col gap-4"> {images.slice(6, 9).map((image, index) => ( <div key={index} className="flex-gallery-item relative overflow-hidden rounded-lg" > <img src={image} alt={`Gallery image ${index + 7}`} className="w-full h-auto object-cover transition-transform hover:scale-105" /> </div> ))} </div> </div> </div> ); }
Best Practices
Optimize for Reusability
Reusability saves development time and reduces maintenance overhead, especially in large projects. By establishing modular flex-based components, you can quickly replicate them throughout your application. Shared components, such as navigation bars, cards, or footers, often rely on consistent flex
behaviors that scale gracefully when new features or subcomponents are introduced.
One step toward reusability involves creating small, specialized React components that encapsulate common layout needs. For instance, a Card component might accept children as props while using a uniform set of flex
classes. This structure ensures that each card remains consistent in spacing, alignment, and background presentation, making it easy to reuse across multiple pages.
Build Responsive Design
Responsiveness is vital in modern web development, ensuring that layouts adapt seamlessly across devices. Flex-based designs lend themselves naturally to responsive patterns because they grow
or shrink
as screen dimensions change. Strategic use of Tailwind’s breakpoint prefixes (like md:flex-1
, etc.) yields flexible designs that display elegantly on phones, tablets, and desktops.
When building responsive flex layouts, consider focusing on grouping content by priority. Elements that are crucial for users, such as action buttons, should remain easily accessible on small screens. On larger displays, you can spread out additional items to optimize available space. This approach keeps your application both functional and visually appealing.
Accessibility Considerations
Enhance Readability and Navigability
Flex utilities directly affect how content is distributed and aligned within a container, improving readability and navigability. By structuring elements logically using these utilities, you can guide users through your interface more effectively.
For example, assigning flex-1
to dynamic sections ensures equal spacing between adjacent items, while flex-none
can lock fixed elements like icons or buttons into place, maintaining a stable layout.
When designing navigational components or complex data displays, use flex utilities alongside spacing utilities like gap
to clarify relationships between elements.
Support Accessible Interactive Elements
Interactive elements like buttons, dropdowns, and modals are easier to manage with flex utilities. Use flex-auto
or flex-1
to distribute space evenly among buttons, while flex-none
works well for fixed elements like close buttons. Adding responsive prefixes (md:flex-none
) ensures that the layout adapts to different screen sizes seamlessly.
For modals and dropdowns, ensure all interactive elements are accessible via keyboard navigation. Use the tabindex="0"
attribute for non-focusable elements(<div>
, etc.) to make them focusable. You can also use aria-hidden
attributes to exclude non-flexible or hidden elements from being read by screen readers, improving the user experience for assistive technologies.