Tailwind CSS Resize
Resize allows elements to be resizable by the user- horizontally, vertically, or both. It typically controls how an element can be dragged or stretched by the end-user, often seen in text areas or other containers.
Tailwind includes a set of utilities that map directly to resize properties, making it easy to apply these transformations in a production-ready environment. Throughout this guide, we will explore how to utilize these utilities in different contexts, showcase how to manage them during interactive states, and introduce ways to adapt them for responsive design.
Class | Properties | Example |
---|---|---|
resize-none | resize: none; | <div className="resize-none"></div> |
resize-y | resize: vertical; | <div className="resize-y"></div> |
resize-x | resize: horizontal; | <div className="resize-x"></div> |
resize | resize: both; | <div className="resize"></div> |
Overview of Resize
All Directions Resizing
Allowing an element to be resized in both height and width can significantly improve the flexibility of your layout. This approach is often used in text editing environments, multi-line input fields, or other scenarios where you anticipate unpredictable content lengths.
export default function BothDirections() { return ( <div className="h-screen w-screen p-8 bg-gradient-to-r from-green-100 to-blue-100 flex items-center justify-center"> <div className="resize overflow-auto border-2 border-green-500 bg-white p-4 shadow-lg"> {/* resize => resize: both; */} <img src="https://images.unsplash.com/photo-1527443224154-c4a3942d3acf" alt="All directions resizing" className="object-cover w-full h-64 rounded" /> <div className="mt-4 text-gray-700"> Feel free to drag the edges of this component in both width and height. </div> </div> </div> ); }
Vertically Resizing
Restricting resizing to a vertical direction is beneficial if your container should only expand in height. This is frequently used for textareas where the text length varies but the element’s width remains consistent to match form layouts or other page structures.
export default function VerticalOnly() { return ( <div className="h-screen w-screen flex items-center justify-center bg-gray-200 p-8"> <textarea className="resize-y border border-blue-500 bg-white p-4 rounded w-96" /* resize-y => resize: vertical; */ rows={4} placeholder="Only vertical resizing is allowed here..." /> </div> ); }
Horizontally Resizing
Some use cases demand a horizontally resizable container—for instance, a sidebar that you want users to be able to expand or contract, but maintain fixed height constraints. This approach is equally relevant for advanced layouts, such as adjustable columns in data tables.
export default function HorizontalOnly() { return ( <div className="h-screen w-screen flex items-center justify-center bg-gray-200 p-8"> <textarea className="resize-x border border-blue-500 bg-white p-4 rounded w-32 h-60" /* resize-x => resize: horizonta; */ rows={4} placeholder="Only horizontal resizing is allowed here..." /> </div> ); }
No Resizing
Sometimes you need to ensure that an element cannot be resized, effectively disabling the user’s ability to modify its dimensions. Even if default styling might allow resizing (e.g., <textarea>
in some browsers), you can override it with a straightforward utility in Tailwind.
export default function NoResize() { return ( <div className="h-screen w-screen flex items-center justify-center bg-gray-50"> <textarea className="resize-none border border-red-400 p-4 w-80 h-32" /* resize-none => resize: none; */ placeholder="Resizing has been disabled for this field..." /> </div> ); }
States and Responsiveness
While we have covered core functionality, styling often needs to adapt based on state changes (like a hover or focus event), and also across different breakpoints. This flexibility ensures that your design remains consistent yet adaptable, especially for complex, interactive UI elements.
Hover and Focus States
Using state modifiers for hover
and focus
states can emphasize the resizable container when the user interacts with it.
export default function StatesEnhancement() { return ( <div className="h-screen w-screen flex items-center justify-center bg-gray-50"> <textarea className="resize-none hover:resize border border-red-400 p-4 w-80 h-32" /* resize-none => resize: none; On hover-> resize => resize: both; */ placeholder="Resizing is enabled on hover..." /> </div> ); }
Breakpoint Modifiers
Breakpoint modifiers in Tailwind allow you to override classes at specific screen widths. For example, you might want to enable horizontal resizing on large screens but disable it on smaller devices to preserve layout integrity.
export default function ResponsiveResize() { return ( <div className="h-screen w-screen flex items-center justify-center bg-gray-50"> { /* lg:resize-x => resize: horizontal for large screens; */} <textarea className="resize-none lg:resize border border-red-400 p-4 w-80 h-32" placeholder="Resizing is enabled only on large screens..." /> </div> ); }
Real World Examples
Dynamic Text Editor with Resize Controls
A text editor component with resizable input area and character count display.
export default function TextEditor() { const initialText = [ { id: 1, placeholder: "Start writing your story...", savedContent: "Lorem ipsum dolor sit amet, consectetur adipiscing elit..." } ]; return ( <div className="p-6 bg-gray-50"> <div className="max-w-3xl mx-auto bg-white rounded-lg shadow-lg p-4"> <textarea className="w-full p-4 border rounded-md resize-y min-h-[200px] max-h-[600px]" placeholder={initialText[0].placeholder} defaultValue={initialText[0].savedContent} /> <div className="mt-2 text-sm text-gray-500 flex justify-between"> <span>Character count: {initialText[0].savedContent.length}</span> <span>Drag bottom edge to resize</span> </div> </div> </div> ); }
Resizable Product Gallery Grid
A responsive product gallery with resizable image containers.
export default function ProductGallery() { const products = [ { id: 1, name: "Premium Leather Wallet", price: "$89.99", src: "https://images.unsplash.com/photo-1627123424574-724758594e93", alt: "Brown leather wallet" }, { id: 2, name: "Vintage Watch", price: "$299.99", src: "https://images.unsplash.com/photo-1524592094714-0f0654e20314", alt: "Classic analog watch" }, { id: 3, name: "Designer Sunglasses", price: "$159.99", src: "https://images.unsplash.com/photo-1572635196237-14b3f281503f", alt: "Fashionable sunglasses" }, { id: 4, name: "Canvas Backpack", price: "$129.99", src: "https://images.unsplash.com/photo-1553062407-98eeb64c6a62", alt: "Durable canvas backpack" }, { id: 5, name: "Silver Necklace", price: "$199.99", src: "https://images.unsplash.com/photo-1515562141207-7a88fb7ce338", alt: "Sterling silver necklace" }, ]; return ( <div className="p-8 bg-gray-100"> <div className="grid grid-cols-1 gap-6"> {products.map((product) => ( <div key={product.id} className="bg-white rounded-lg overflow-hidden shadow-md"> <div className="resize-y min-h-[200px] max-h-[400px] overflow-hidden"> <img src={product.src} alt={product.alt} className="w-full h-full object-cover" /> </div> <div className="p-4"> <h3 className="font-semibold">{product.name}</h3> <p className="text-blue-600">{product.price}</p> </div> </div> ))} </div> </div> ); }
Reviews Dashboard
A resizable textarea component for customer service representatives to manage product reviews.
const ReviewsDashboard = () => { const data = [ { id: 1, product: "Wireless Headphones", customer: "Emily Parker", rating: 4, review: "Great sound quality but battery life could be better", response: "Thank you for your feedback about the battery life.", image: "https://images.unsplash.com/photo-1505740420928-5e560c06d30e" }, { id: 2, product: "Smart Watch", customer: "Michael Chen", rating: 5, review: "Perfect fitness companion!", response: "We're glad you're enjoying the fitness features!", image: "https://images.unsplash.com/photo-1523275335684-37898b6baf30" }, { id: 3, product: "Laptop Stand", customer: "Sarah Johnson", rating: 3, review: "Good build quality but could be more adjustable", response: "", image: "https://images.unsplash.com/photo-1527864550417-7fd91fc51a46" }, { id: 4, product: "Wireless Mouse", customer: "David Wilson", rating: 5, review: "Exactly what I needed for my work setup", response: "", image: "https://images.unsplash.com/photo-1615663245857-ac93bb7c39e7" }, { id: 5, product: "Mechanical Keyboard", customer: "Lisa Zhang", rating: 4, review: "Love the tactile feedback, slightly loud though", response: "", image: "https://images.unsplash.com/photo-1511467687858-23d96c32e4ae" }, { id: 6, product: "USB-C Hub", customer: "Robert Brown", rating: 4, review: "Great connectivity options, runs a bit warm", response: "", image: "https://images.unsplash.com/photo-1544244015-0df4b3ffc6b0" } ]; return ( <div className="p-6 max-w-6xl mx-auto"> <h1 className="text-2xl font-bold mb-6">Customer Reviews Dashboard</h1> <div className="grid gap-6"> {data.map((item) => ( <div key={item.id} className="bg-white p-4 rounded-lg shadow"> <div className="flex items-start gap-4"> <img src={item.image} alt={item.product} className="w-24 h-24 object-cover rounded" /> <div className="flex-1"> <h3 className="font-semibold">{item.product}</h3> <div className="text-sm text-gray-600 mb-2"> Customer: {item.customer} | Rating: {item.rating}/5 </div> <p className="text-gray-800 mb-3">{item.review}</p> <textarea className="w-full p-2 border rounded resize-y min-h-[100px] focus:ring-2 focus:ring-blue-500" placeholder="Write your response..." defaultValue={item.response} /> </div> </div> </div> ))} </div> </div> ); }; export default ReviewsDashboard;
Notes Application
A note-taking interface with resizable text areas for each note, allowing users to adjust the height based on content length.
const NotesApp = () => { const data = [ { id: 1, title: "Project Ideas", content: `1. Build a recipe app 2. Create a budget tracker 3. Design a portfolio website`, category: "Work", color: "bg-blue-100", icon: "https://images.unsplash.com/photo-1512314889357-e157c22f938d" }, { id: 2, title: "Meeting Notes", content: `Discuss Q2 goals Review team performance Plan upcoming sprint`, category: "Work", color: "bg-yellow-100", icon: "https://images.unsplash.com/photo-1517245386807-bb43f82c33c4" }, { id: 3, title: "Book Recommendations", content: `1. The Pragmatic Programmer 2. Clean Code 3. Design Patterns`, category: "Learning", color: "bg-purple-100", icon: "https://images.unsplash.com/photo-1544716278-ca5e3f4abd8c" }, { id: 4, title: "Workout Plan", content: `Monday: Upper body Wednesday: Lower body Friday: Cardio`, category: "Health", color: "bg-red-100", icon: "https://images.unsplash.com/photo-1517836357463-d25dfeac3438" }, { id: 5, title: "Travel Plans", content: `Research destinations Check flight prices Plan itinerary`, category: "Personal", color: "bg-indigo-100", icon: "https://images.unsplash.com/photo-1488646953014-85cb44e25828" } ]; return ( <div className="min-h-screen bg-gray-50 p-8"> <div className="max-w-7xl mx-auto"> <div className="flex justify-between items-center mb-8"> <h1 className="text-3xl font-bold">My Notes</h1> <div className="flex gap-4"> <select className="px-4 py-2 rounded border"> <option>All Categories</option> <option>Work</option> <option>Personal</option> <option>Learning</option> <option>Health</option> </select> <button className="px-4 py-2 bg-blue-600 text-white rounded"> New Note </button> </div> </div> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> {data.map((note) => ( <div key={note.id} className={`${note.color} rounded-lg p-6 shadow-sm`}> <div className="flex items-center gap-3 mb-4"> <img src={note.icon} alt={note.category} className="w-8 h-8 rounded" /> <div> <h3 className="font-semibold">{note.title}</h3> <span className="text-sm text-gray-600">{note.category}</span> </div> </div> <textarea className="w-full bg-white/50 rounded p-3 resize-y min-h-[120px] focus:ring-2 focus:ring-blue-500" defaultValue={note.content} /> <div className="flex justify-end mt-4 gap-2"> <button className="text-gray-600 hover:text-gray-900"> Edit </button> <button className="text-red-600 hover:text-red-900"> Delete </button> </div> </div> ))} </div> </div> </div> ); }; export default NotesApp;
Chat Application
A modern chat interface with resizable message input area.
const ChatApp = () => { const data = [ { id: 1, name: "Alice Cooper", avatar: "https://images.unsplash.com/photo-1494790108377-be9c29b29330", status: "online", lastMessage: "Hey, are we still meeting today?", time: "2:30 PM", unread: 2 }, { id: 2, name: "Bob Wilson", avatar: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e", status: "offline", lastMessage: "Thanks for the update!", time: "Yesterday", unread: 0 }, { id: 3, name: "Carol Martinez", avatar: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80", status: "online", lastMessage: "The project files are ready for review", time: "9:15 AM", unread: 1 }, { id: 4, name: "David Chang", avatar: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d", status: "busy", lastMessage: "Let's schedule a call next week", time: "Tuesday", unread: 0 }, { id: 5, name: "Eva Green", avatar: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80", status: "online", lastMessage: "Perfect, I'll take care of it", time: "Monday", unread: 0 }, { id: 6, name: "Frank Johnson", avatar: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e", status: "offline", lastMessage: "How about the budget report?", time: "Last week", unread: 0 } ]; return ( <div className="h-screen flex flex-col sm:flex-row bg-gray-100"> {/* Sidebar - Full width on mobile, side panel on desktop */} <div className="w-full sm:w-80 bg-white border-b sm:border-r sm:min-h-screen"> <div className="p-3 sm:p-4"> <div className="flex items-center justify-between mb-4"> <h2 className="text-lg font-bold">Messages</h2> <button className="p-1.5 hover:bg-gray-100 rounded-full text-blue-600"> <span className="text-xl">+</span> </button> </div> <div className="space-y-1"> {data.map((chat) => ( <div key={chat.id} className="flex items-center gap-2 p-2 hover:bg-gray-50 rounded-lg cursor-pointer" > <div className="relative flex-shrink-0"> <img src={chat.avatar} alt={chat.name} className="w-10 h-10 rounded-full object-cover" /> <span className={`absolute bottom-0 right-0 w-2.5 h-2.5 rounded-full border-2 border-white ${chat.status === 'online' ? 'bg-green-500' : chat.status === 'busy' ? 'bg-red-500' : 'bg-gray-500'}`} /> </div> <div className="flex-1 min-w-0"> <div className="flex justify-between items-baseline"> <h3 className="font-medium text-sm truncate">{chat.name}</h3> <span className="text-xs text-gray-500 ml-1 flex-shrink-0">{chat.time}</span> </div> <p className="text-xs text-gray-600 truncate">{chat.lastMessage}</p> </div> {chat.unread > 0 && ( <span className="bg-blue-500 text-white text-xs w-5 h-5 flex items-center justify-center rounded-full flex-shrink-0"> {chat.unread} </span> )} </div> ))} </div> </div> </div> {/* Main Chat Area */} <div className="flex-1 flex flex-col min-w-0"> {/* Chat Header */} <div className="bg-white border-b p-3"> <div className="flex items-center gap-2"> <img src={data[0].avatar} alt={data[0].name} className="w-8 h-8 rounded-full" /> <div className="min-w-0"> <h3 className="font-medium text-sm truncate">{data[0].name}</h3> <span className="text-xs text-green-500">Online</span> </div> </div> </div> {/* Messages Area */} <div className="flex-1 overflow-y-auto p-3"> {/* Message bubbles would go here */} </div> {/* Message Input */} <div className="sticky bottom-0 bg-white border-t p-2 sm:p-3"> <textarea className="w-full p-2.5 text-sm border rounded-lg resize-y min-h-[44px] max-h-[160px] focus:ring-2 focus:ring-blue-500 focus:border-blue-500 mb-2" placeholder="Type your message..." aria-label="Message input" style={{ lineHeight: '1.2' }} /> <div className="flex items-center justify-between"> <div className="flex items-center gap-1.5"> <button className="p-2 hover:bg-gray-100 rounded-full text-gray-600" aria-label="Attach file" > 📎 </button> <button className="p-2 hover:bg-gray-100 rounded-full text-gray-600" aria-label="Add emoji" > 😊 </button> </div> <button className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2" aria-label="Send message" > Send </button> </div> </div> </div> </div> ); }; export default ChatApp;
Best Practices
Leverage Utility Combinations
Tailwind's utility classes can be combined thoughtfully for sophisticated styling. Pairing resize utilities with padding, border, or background classes ensures a resizable element remains visually appealing. For instance, you might use resize-y
with border, rounded corners, and hover effects to create an engaging, modern input field that clearly signals its interactivity.
Combining multiple utilities lets you unify your brand’s design language. By systematically layering classes, you can quickly build components that integrate resizing, spacing, color interactions, and typography without reverting to heavy custom styles.
Build Responsive Design
Responsive design ensures that resizing remains meaningful across a broad range of devices and screen sizes. Tailwind’s breakpoint modifiers, such as md
, lg
, etc., let you toggle resizing behavior based on screen width. A container may allow two-dimensional scaling only on desktops, while restricting it entirely on small mobile screens to preserve layout stability.
The key is to identify where resizing enhances usability for your target audience. On smaller touchscreens, pinch-zoom might be more intuitive than dragging edges. Meanwhile, mid-sized tablets or laptops can benefit from partial resizing to accommodate content. By tailoring each breakpoint setting, you can deliver a more intuitive experience.
Accessibility Considerations
Enhance Readability and Navigability
Resizing in Tailwind can greatly enhance readability, particularly for users who need to enlarge text or interface elements for better accessibility. The ability to drag the element size ensures that content remains well-structured, prevents overflow or clipping, and supports better comprehension.
Additionally, thoughtfully selected font families, weights, and sizes play a vital role in creating a comfortable reading experience. Navigability is just as crucial—while expandable or contractible containers offer layout flexibility, they should be designed to avoid visual confusion or overlapping with nearby elements, ensuring a user-friendly interface.
Focus on High Contrast
High contrast in resizable input fields is vital for accessibility and user clarity, ensuring that users can easily distinguish interactive elements from the surrounding content. This not only supports users with low vision or color blindness but also guarantees that form elements remain easily identifiable, even when resized.
To achieve these accessibility standards, it's crucial to select color pairings that meet or exceed the WCAG contrast ratio of 4.5:1
. Tailwind CSS provides a large color palette and also offers a configuration file where you can define and extend your color palette to ensure compliance with accessibility guidelines.