Tailwind CSS User Select
The user-select
property in CSS is a powerful tool that controls how users interact with text on a webpage. It determines whether a user can select, copy, or interact with text elements.
Tailwind CSS simplifies this functionality by providing a set of utilities for managing user-select
properties effortlessly.
In this guide, we'll explore the different ways you can utilize User Select utilities in Tailwind CSS, including practical implementations and conditional application based on states and breakpoints.
Class | Properties | Example |
---|---|---|
select-none | user-select: none; | <div className="select-none"></div> |
select-text | user-select: text; | <div className="select-text"></div> |
select-all | user-select: all; | <div className="select-all"></div> |
select-auto | user-select: auto; | <div className="select-auto"></div> |
Overview of User Select
Adding the select-none
The select-none
utility is used to disable the user from selecting the text.
// Disable text selection for a card export default function App() { return ( <div className="h-screen w-screen bg-gray-100 flex items-center justify-center"> <div className="p-6 bg-white rounded shadow-md"> <img src="https://images.unsplash.com/photo-1517059224940-d4af9eec41b7" alt="example" className="rounded" /> <p className="select-none mt-4 text-gray-700"> This text cannot be selected or copied because of the `select-none` utility. </p> </div> </div> ); } // CSS Property applied here: // user-select: none;
Adding the select-text
The select-text
utility is used to enable the user to select text. This is most useful when applied conditionally, e.g., enabled on large screens.
// Enable partial text selection export default function App() { return ( <div className="h-screen w-screen bg-gray-100 flex items-center justify-center"> <div className="p-6 bg-white rounded shadow-md"> <img src="https://images.unsplash.com/photo-1517059224940-d4af9eec41b7" alt="example" className="rounded" /> <p className="select-text mt-4 text-gray-700"> Selecting this text is enabled. You can highlight this for copy-paste functionality. </p> </div> </div> ); } // CSS Property applied here: // user-select: text;
Adding the select-all
// Select all text in a div export default function App() { return ( <div className="h-screen w-screen bg-gray-100 flex items-center justify-center"> <div className="p-6 bg-white rounded shadow-md"> <img src="https://images.unsplash.com/photo-1517059224940-d4af9eec41b7" alt="example" className="rounded" /> <p className="select-all mt-4 text-gray-700"> Click anywhere on this text to select all the content. </p> </div> </div> ); } // CSS Property applied here: // user-select: all;
The select-all
utility offers a quick way to enable users to select all content within an element with a single click.
Adding the select-auto
// Use auto-selection behavior export default function App() { return ( <div className="h-screen w-screen bg-gray-100 flex items-center justify-center"> <div className="p-6 bg-white rounded shadow-md"> <img src="https://images.unsplash.com/photo-1517059224940-d4af9eec41b7" alt="example" className="rounded" /> <p className="select-auto mt-4 text-gray-700"> The text selection behavior in this box follows the browser's default operation. </p> </div> </div> ); } // CSS Property applied here: // user-select: auto;
States and Responsiveness
Tailwind CSS also enables developers to apply User Select utilities conditionally, based on states such as hover or focus, and responsive designs adapting to screen sizes.
Hover and Focus States
Conditional states like hover:
or focus:
let you control the User Select behavior dynamically.
// Conditional hover state for text selection export default function App() { return ( <div className="h-screen w-screen bg-gray-100 flex items-center justify-center"> <div className="p-6 bg-white rounded shadow-md"> <img src="https://images.unsplash.com/photo-1517059224940-d4af9eec41b7" alt="example" className="rounded" /> <p className="hover:select-none mt-4 text-gray-700"> Try hovering over this text block. The ability to select the content is disabled in the hover state. </p> </div> </div> ); } // CSS Property applied here on hover: // user-select: none;
Breakpoint Modifiers
Breakpoint modifiers permit adjustments in User Select behavior based on screen size thresholds.
// Change selection behavior across screen sizes export default function App() { return ( <div className="h-screen w-screen bg-gray-100 flex items-center justify-center"> <div className="p-6 bg-white rounded shadow-md"> <img src="https://images.unsplash.com/photo-1517059224940-d4af9eec41b7" alt="example" className="rounded" /> <p className=" sm:select-text md:select-all lg:select-auto xl:select-none mt-4 text-gray-700"> On a small screen, text selection is standard. On medium, selecting the entire block is easier. With larger screens, the behavior adapts to being conditional. </p> </div> </div> ); } /* CSS Property applied based on breakpoints: - Small screens -> user-select: text; - Medium screens -> user-select: all; - Large screens -> user-select: auto; - Extra-large -> user-select: none; */
Real World Examples
Product Catalog with Copyable SKUs
A product listing page where product names are selectable but SKU codes are protected from accidental selection.
export default function ProductCatalog() { const products = [ { id: 1, name: 'Premium Leather Wallet', sku: 'LW-2023-001', price: '$89.99', image: 'https://images.unsplash.com/photo-1627123424574-724758594e93' }, { id: 2, name: 'Vintage Watch Collection', sku: 'VW-2023-002', price: '$299.99', image: 'https://images.unsplash.com/photo-1524592094714-0f0654e20314' }, { id: 3, name: 'Designer Sunglasses', sku: 'DS-2023-003', price: '$159.99', image: 'https://images.unsplash.com/photo-1572635196237-14b3f281503f' }, { id: 4, name: 'Handcrafted Backpack', sku: 'HB-2023-004', price: '$129.99', image: 'https://images.unsplash.com/photo-1553062407-98eeb64c6a62' }, { id: 5, name: 'Premium Headphones', sku: 'PH-2023-005', price: '$249.99', image: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e' }, { id: 6, name: 'Smart Fitness Tracker', sku: 'SF-2023-006', price: '$179.99', image: 'https://images.unsplash.com/photo-1557438159-51eec7a6c9e8' } ]; return ( <div className="p-6 bg-gray-50"> <h2 className="text-2xl font-bold mb-6">Product Catalog</h2> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> {products.map(product => ( <div key={product.id} className="bg-white p-4 rounded-lg shadow"> <img src={product.image} alt={product.name} className="w-full h-48 object-cover rounded" /> <h3 className="text-lg font-semibold mt-2 select-text">{product.name}</h3> <p className="text-gray-600 select-none">SKU: {product.sku}</p> <p className="text-blue-600 font-bold mt-2">{product.price}</p> </div> ))} </div> </div> ); }
Interactive Recipe Card
A recipe card interface where ingredients are selectable for shopping lists, but cooking instructions are protected.
export default function RecipeCollection() { const recipes = [ { id: 1, title: 'Classic Margherita Pizza', ingredients: ['Fresh Mozzarella', 'Basil Leaves', 'Tomato Sauce', 'Olive Oil', 'Pizza Dough', 'Salt'], instructions: '1. Preheat oven to 450°F\n2. Roll out dough\n3. Add toppings\n4. Bake for 15 minutes', image: 'https://images.unsplash.com/photo-1513104890138-7c749659a591' }, { id: 2, title: 'Vegetarian Buddha Bowl', ingredients: ['Quinoa', 'Chickpeas', 'Sweet Potato', 'Kale', 'Avocado', 'Tahini'], instructions: '1. Cook quinoa\n2. Roast vegetables\n3. Arrange in bowl\n4. Drizzle sauce', image: 'https://images.unsplash.com/photo-1512621776951-a57141f2eefd' }, ]; return ( <div className="p-6 bg-gray-100"> <div className="max-w-4xl mx-auto"> {recipes.map(recipe => ( <div key={recipe.id} className="bg-white rounded-lg shadow-lg mb-6 overflow-hidden"> <img src={recipe.image} alt={recipe.title} className="w-full h-64 object-cover" /> <div className="p-6"> <h3 className="text-2xl font-bold mb-4 select-text">{recipe.title}</h3> <div className="mb-4"> <h4 className="font-semibold mb-2">Ingredients:</h4> <ul className="select-text"> {recipe.ingredients.map((ingredient, index) => ( <li key={index} className="ml-4">• {ingredient}</li> ))} </ul> </div> <div> <h4 className="font-semibold mb-2">Instructions:</h4> <pre className="select-none whitespace-pre-wrap text-gray-700">{recipe.instructions}</pre> </div> </div> </div> ))} </div> </div> ); }
Document Library Interface
A document management interface where document titles are selectable but action buttons are protected.
export default function DocumentLibrary() { const documents = [ { id: 1, title: 'Annual Financial Report 2023', type: 'PDF', size: '2.4 MB', lastModified: '2023-12-01', icon: 'https://images.unsplash.com/photo-1618477388954-7852f32655ec' }, { id: 2, title: 'Product Roadmap 2024', type: 'PPTX', size: '5.2 MB', lastModified: '2023-11-25', icon: 'https://images.unsplash.com/photo-1572044162444-ad60f128bdea' }, { id: 3, title: 'Employee Handbook', type: 'PDF', size: '3.1 MB', lastModified: '2023-11-20', icon: 'https://images.unsplash.com/photo-1586281380349-632531db7ed4' }, { id: 4, title: 'Sales Pipeline Report', type: 'XLSX', size: '1.5 MB', lastModified: '2023-11-15', icon: 'https://images.unsplash.com/photo-1551288049-bebda4e38f71' }, { id: 5, title: 'Brand Guidelines', type: 'PDF', size: '8.7 MB', lastModified: '2023-11-10', icon: 'https://images.unsplash.com/photo-1572044162444-ad60f128bdea' } ]; return ( <div className="p-6 bg-gray-50"> <div className="max-w-5xl mx-auto"> <h2 className="text-2xl font-bold mb-6">Document Library</h2> <div className="bg-white rounded-lg shadow"> {documents.map(doc => ( <div key={doc.id} className="border-b border-gray-200 p-4 flex items-center justify-between hover:bg-gray-50"> <div className="flex items-center space-x-4"> <img src={doc.icon} alt={doc.type} className="w-8 h-8 object-cover" /> <div> <h3 className="font-medium select-text">{doc.title}</h3> <p className="text-sm text-gray-500 select-none">{doc.type} • {doc.size}</p> </div> </div> <div className="flex space-x-2 select-none"> <button className="px-3 py-1 text-sm text-blue-600 hover:bg-blue-50 rounded">View</button> <button className="px-3 py-1 text-sm text-green-600 hover:bg-green-50 rounded">Download</button> </div> </div> ))} </div> </div> </div> ); }
Team Directory Cards
A team directory where members' contact details are selected on click.
export default function TeamDirectory() { const teamMembers = [ { id: 1, name: 'Sarah Johnson', role: 'Product Manager', email: 'sarah.j@company.com', phone: '(555) 123-4567', avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330' }, { id: 2, name: 'Michael Chen', role: 'Senior Developer', email: 'michael.c@company.com', phone: '(555) 234-5678', avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d' }, { id: 3, name: 'Emily Rodriguez', role: 'UX Designer', email: 'emily.r@company.com', phone: '(555) 345-6789', avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb' }, { id: 4, name: 'David Kim', role: 'Marketing Director', email: 'david.k@company.com', phone: '(555) 456-7890', avatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e' }, { id: 5, name: 'Lisa Thompson', role: 'Sales Manager', email: 'lisa.t@company.com', phone: '(555) 567-8901', avatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80' }, { id: 6, name: 'James Wilson', role: 'Content Strategist', email: 'james.w@company.com', phone: '(555) 678-9012', avatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e' } ]; return ( <div className="p-8 bg-gray-100"> <div className="max-w-6xl mx-auto"> <h2 className="text-3xl font-bold mb-8 text-center">Our Team</h2> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> {teamMembers.map(member => ( <div key={member.id} className="bg-white rounded-xl shadow-lg overflow-hidden"> <div className="p-4"> <img src={member.avatar} alt={member.name} className="w-32 h-32 rounded-full mx-auto object-cover" /> <div className="text-center mt-4"> <h3 className="text-xl font-semibold select-text">{member.name}</h3> <p className="text-gray-600 select-text">{member.role}</p> <div className="mt-4 space-y-2"> <p className="select-all text-sm text-gray-500">{member.email}</p> <p className="select-all text-sm text-gray-500">{member.phone}</p> </div> </div> </div> </div> ))} </div> </div> </div> ); }
Event Schedule Timeline
An event schedule where event titles and descriptions are selectable but time slots are protected.
export default function EventSchedule() { const events = [ { id: 1, title: 'Keynote: Future of Technology', time: '9:00 AM', speaker: 'Dr. Amanda White', location: 'Main Hall', description: 'Exploring emerging tech trends and their impact on society', icon: 'https://images.unsplash.com/photo-1505373877841-8d25f7d46678' }, { id: 2, title: 'Workshop: AI Implementation', time: '10:30 AM', speaker: 'Prof. John Smith', location: 'Room 201', description: 'Hands-on session on implementing AI in business', icon: 'https://images.unsplash.com/photo-1488590528505-98d2b5aba04b' }, { id: 3, title: 'Panel Discussion: Cybersecurity', time: '1:00 PM', speaker: 'Multiple Experts', location: 'Conference Room A', description: 'Industry experts discuss current security challenges', icon: 'https://images.unsplash.com/photo-1563986768609-322da13575f3' }, { id: 4, title: 'Networking Lunch', time: '12:00 PM', speaker: 'All Attendees', location: 'Garden Area', description: 'Connect with industry professionals over lunch', icon: 'https://images.unsplash.com/photo-1505373877841-8d25f7d46678' }, { id: 5, title: 'Product Launch', time: '2:30 PM', speaker: 'Sarah Johnson', location: 'Exhibition Hall', description: 'Unveiling our latest innovation in tech', icon: 'https://images.unsplash.com/photo-1460925895917-afdab827c52f' }, { id: 6, title: 'Closing Remarks', time: '4:00 PM', speaker: 'CEO Mark Thompson', location: 'Main Hall', description: 'Summary and future directions', icon: 'https://images.unsplash.com/photo-1505373877841-8d25f7d46678' } ]; return ( <div className="p-8 bg-gray-50"> <div className="max-w-4xl mx-auto"> <h2 className="text-3xl font-bold mb-8 text-center">Event Schedule</h2> <div className="space-y-6"> {events.map(event => ( <div key={event.id} className="bg-white rounded-lg shadow-md p-6"> <div className="flex items-start space-x-4"> <img src={event.icon} alt="" className="w-12 h-12 rounded-full object-cover" /> <div className="flex-1"> <div className="flex justify-between items-start"> <div> <h3 className="text-xl font-semibold select-text">{event.title}</h3> <p className="text-gray-600 mt-1 select-text">{event.description}</p> </div> <span className="text-blue-600 font-semibold select-none">{event.time}</span> </div> <div className="mt-4 flex items-center space-x-4 text-sm text-gray-500 select-none"> <span>📍 {event.location}</span> <span>👤 {event.speaker}</span> </div> </div> </div> </div> ))} </div> </div> </div> ); }
Best Practices
Maintain Design Consistency
Define guidelines within your design system specifying when and where select-*
should be applied. For example, applying select-none
to UI elements like buttons while allowing content areas to remain selectable creates a predictable experience for users.
Additionally, use select-*
alongside cursor-*
utilities to further refine design. When text selection is disabled, changing the cursor to cursor-not-allowed
communicates the restriction more effectively. Similarly, for content that is meant to be copied, using cursor-pointer
reinforces the interactive affordance.
Optimize for Reusability
Reusability is a key principle in designing scalable interfaces, and select-*
should be implemented in a way that adapts to different use cases. Creating reusable components that consistently apply selection behavior ensures that developers do not have to repeat utility classes throughout the codebase.
For example, a SelectableText
component can encapsulate select-all
behavior, ensuring that users can easily copy text wherever needed. This is particularly useful for displaying code snippets or reference text where full text selection is beneficial.
Similarly, a NonSelectableLabel
component can apply select-none
, preventing accidental text selection on elements like disabled buttons or decorative UI components.
Accessibility Considerations
Enhance Readability and Navigability
Ensuring that text remains readable and easy to interact with is essential for accessibility. When using Tailwind’s select-none
, avoid applying it to important content such as instructional text or descriptions, as this may prevent users from selecting and referencing critical information. Instead, restrict select-none
to decorative elements- such as branding text.
Conversely, select-all
can improve usability in areas where users frequently copy content, such as code snippets or reference data. This utility allows all text inside an element to be automatically selected when clicked, making it easier to copy with a single action.
Support Accessible Interactive Elements
In interactive components, text selection behavior should align with user expectations. Applying select-none
to disabled buttons prevents accidental text selection, which can disrupt the user experience. This is especially important for mobile users, where unintended text selection often occurs when tapping elements.
Another key consideration is the accessibility of tooltips, modals, and popovers. Tooltips often contain supplementary information, such as API keys or secret codes, that users may need to copy. In such cases, applying select-all
ensures easy selection.