Menu

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.

ClassPropertiesExample
select-noneuser-select: none;<div className="select-none"></div>
select-textuser-select: text;<div className="select-text"></div>
select-alluser-select: all;<div className="select-all"></div>
select-autouser-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.

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

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

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

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

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

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

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

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

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

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

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