Menu

Tailwind CSS Visibility

Visibility determines whether an element is visible or hidden in the DOM. This property does not remove the element from the document flow. Instead, it merely toggles its visibility while preserving its space.

In this guide, we'll explore how to use the visibility utilities in Tailwind CSS, delve into conditional states like hover and focus, and show how to adapt visibility to different screen sizes using media queries.

ClassPropertiesExample
visiblevisibility: visible;<div className="visible"></div>
invisiblevisibility: hidden;<div className="invisible"></div>
collapsevisibility: collapse;<div className="collapse"></div>

Overview of Visibility

Invisible Elements

You can make an element visually hidden but still preserve its space in the DOM with the invisible utility.

In the below example, applying invisible keeps the image's dimensions intact but hides it visually.

This is a live editor. Play around with it!
export default function InvisibleElement() {
  return (
    <div className="w-screen h-screen flex justify-center items-center bg-gray-100">
      {/* The following element is hidden */}
      <img 
        src="https://images.unsplash.com/photo-1677086813101-496781a0f327" 
        alt="Hidden Landscape" 
        className="invisible w-60 h-60 object-cover"
      />
      {/* invisibility doesn't destroy layout */}
      <p className="text-gray-700">This text stays aligned to the image space.</p>
    </div>
  );
}

Collapsible Elements

When you want to hide table, table rows, row groups, columns, and column groups and remove their space, use the collapse utility. This works similar to display: none in tables:

In the below example, remove the collapse utility from the table to make it visible again:

This is a live editor. Play around with it!
export default function CollapsingTableBorders() {
  return (
    <div className="w-screen h-screen p-10 bg-gray-200">
      <table className="collapse">
        <thead>
          <tr>
            <th className="border border-gray-500 px-4 py-2">Name</th>
            <th className="border border-gray-500 px-4 py-2">Age</th>
            <th className="border border-gray-500 px-4 py-2">Location</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td className="border border-gray-500 px-4 py-2">Alice</td>
            <td className="border border-gray-500 px-4 py-2">30</td>
            <td className="border border-gray-500 px-4 py-2">New York</td>
          </tr>
          <tr>
            <td className="border border-gray-500 px-4 py-2">Bob</td>
            <td className="border border-gray-500 px-4 py-2">25</td>
            <td className="border border-gray-500 px-4 py-2">San Francisco</td>
          </tr>
        </tbody>
      </table>
      <p className="text-gray-800 mt-4">
        This table uses the <code>collapse</code>.
      </p>
    </div>
  );
}

Visible Elements

Using the visible utility, you can reset a hidden element to make it visible again in Tailwind CSS. By explicitly using visible, you're ensuring that the element is displayed.

This is a live editor. Play around with it!
export default function ForceVisible() {
  return (
    <div className="w-screen h-screen bg-white flex justify-center items-center">
      {/* Visible Image */}
      <img 
        src="https://images.unsplash.com/photo-1677086813101-496781a0f327" 
        alt="Visible Landscape" 
        className="visible w-64 h-64 object-cover"
      />
    </div>
  );
}

States and Responsiveness

Tailwind's utilities shine when you need to apply visibility dynamically. Tailwind CSS allows combining states and responsive breakpoints for added clarity.

Hover and Focus States

Implement dynamic visibility changes based on states like hover, focus, or active:

This is a live editor. Play around with it!
export default function StateBasedVisibility() {
  return (
    <div className="w-screen h-screen flex justify-center items-center bg-gray-50">
      <button className="visible hover:invisible relative w-80 h-16 bg-blue-500 text-white">
        Hover to make the button invisible
      </button>
    </div>
  );
}

Breakpoint Modifiers

Tailwind enables you to control visibility responsively with breakpoints. For instance, hide an element on small screens but show it on larger ones.

Here, the sm:visible makes the image visible only for screens larger than the sm breakpoint.

This is a live editor. Play around with it!
export default function ResponsiveVisibility() {
  return (
    <div className="w-screen h-screen bg-gray-100">
      {/* Image Hidden on Small Screens */}
      <img 
        src="https://images.unsplash.com/photo-1677086813101-496781a0f327" 
        alt="Responsive Landscape" 
        className="invisible lg:visible w-60 h-60 object-cover"
      />
      <p className="text-center text-gray-700 mt-6">
        This image becomes visible on larger screens.
      </p>
    </div>
  );
}

Real World Examples

Toggle Product Availability Status

This component displays a list of products with their availability status that can be toggled between visible and invisible states.

This is a live editor. Play around with it!
export default function ProductAvailability() {
  const products = [
    { id: 1, name: "Wireless Headphones", price: "$199", status: "In Stock", image: "https://images.unsplash.com/photo-1505740420928-5e560c06d30e" },
    { id: 2, name: "Smart Watch", price: "$299", status: "Out of Stock", image: "https://images.unsplash.com/photo-1523275335684-37898b6baf30" },
    { id: 3, name: "Laptop Pro", price: "$1299", status: "In Stock", image: "https://images.unsplash.com/photo-1541807084-5c52b6b3adef" },
    { id: 4, name: "Gaming Console", price: "$499", status: "Limited", image: "https://images.unsplash.com/photo-1486401899868-0e435ed85128" },
    { id: 5, name: "Tablet XL", price: "$799", status: "In Stock", image: "https://images.unsplash.com/photo-1544244015-0df4b3ffc6b0" },
    { id: 6, name: "Camera DSLR", price: "$899", status: "Out of Stock", image: "https://images.unsplash.com/photo-1516035069371-29a1b244cc32" }
  ];

  return (
    <div className="grid gap-4 p-6">
      {products.map((product) => (
        <div key={product.id} className="border p-4 rounded-lg">
          <img src={product.image} alt={product.name} className="w-full h-48 object-cover" />
          <h3 className="text-lg font-bold mt-2">{product.name}</h3>
          <p className="text-gray-600">{product.price}</p>
          <span className={`${product.status === "Out of Stock" ? "invisible" : "visible"} 
            inline-block px-2 py-1 mt-2 rounded bg-green-100 text-green-800`}>
            {product.status}
          </span>
        </div>
      ))}
    </div>
  );
}

Conditional Navigation Menu

This component shows a responsive navigation menu that toggles visibility based on screen size and user interaction.

This is a live editor. Play around with it!
export default function NavigationMenu() {
  const menuItems = [
    { id: 1, name: "Dashboard", icon: "https://images.unsplash.com/photo-1517694712202-14dd9538aa97" },
    { id: 2, name: "Products", icon: "https://images.unsplash.com/photo-1542291026-7eec264c27ff" },
    { id: 3, name: "Analytics", icon: "https://images.unsplash.com/photo-1551288049-bebda4e38f71" },
    { id: 4, name: "Settings", icon: "https://images.unsplash.com/photo-1507925921958-8a62f3d1a50d" },
    { id: 5, name: "Support", icon: "https://images.unsplash.com/photo-1486312338219-ce68d2c6f44d" },
    { id: 6, name: "Profile", icon: "https://images.unsplash.com/photo-1519085360753-af0119f7cbe7" }
  ];

  return (
    <nav className="bg-gray-800 text-white p-4">
      <div className="flex justify-between items-center">
        <h1 className="text-xl font-bold">Dashboard</h1>
        <button className="md:invisible visible">Menu</button>
      </div>
      <ul className="invisible md:visible flex flex-col md:flex-row gap-4 mt-4">
        {menuItems.map((item) => (
          <li key={item.id} className="flex items-center gap-2 cursor-pointer hover:bg-gray-700 p-2 rounded">
            <img src={item.icon} alt={item.name} className="w-6 h-6 rounded" />
            <span>{item.name}</span>
          </li>
        ))}
      </ul>
    </nav>
  );
}

Notification System

This component displays a notification system with visibility controls for different types of alerts.

This is a live editor. Play around with it!
export default function NotificationSystem() {
  const notifications = [
    { id: 1, type: "success", message: "Order successfully processed", time: "Just now" },
    { id: 2, type: "error", message: "Payment failed", time: "5 min ago" },
    { id: 3, type: "warning", message: "Low stock alert", time: "10 min ago" },
    { id: 4, type: "info", message: "New feature available", time: "1 hour ago" },
    { id: 5, type: "success", message: "Backup completed", time: "2 hours ago" },
    { id: 6, type: "warning", message: "System maintenance scheduled", time: "3 hours ago" }
  ];

  return (
    <div className="fixed top-4 right-4 w-80">
      {notifications.map((notification) => (
        <div 
          key={notification.id}
          className={`${notification.type === "error" ? "visible" : "invisible"}
            mb-2 p-4 rounded-lg shadow-lg
            ${notification.type === "success" ? "bg-green-100 text-green-800" :
            notification.type === "error" ? "bg-red-100 text-red-800" :
            notification.type === "warning" ? "bg-yellow-100 text-yellow-800" :
            "bg-blue-100 text-blue-800"}`}
        >
          <div className="flex justify-between items-center">
            <p>{notification.message}</p>
            <span className="text-sm opacity-75">{notification.time}</span>
          </div>
        </div>
      ))}
    </div>
  );
}

Form Validation Feedback

This component shows form validation feedback with visibility controls for error messages.

This is a live editor. Play around with it!
export default function FormValidation() {
  const formFields = [
    { id: 1, name: "username", label: "Username", type: "text", error: "Username is required" },
    { id: 2, name: "email", label: "Email", type: "email", error: "Invalid email format" },
    { id: 3, name: "password", label: "Password", type: "password", error: "Password must be 8 characters" },
    { id: 4, name: "phone", label: "Phone", type: "tel", error: "Invalid phone number" },
    { id: 5, name: "address", label: "Address", type: "text", error: "Address is required" },
    { id: 6, name: "city", label: "City", type: "text", error: "City is required" }
  ];

  return (
    <form className="max-w-md mx-auto p-6 space-y-4">
      {formFields.map((field) => (
        <div key={field.id} className="space-y-1">
          <label className="block text-sm font-medium text-gray-700">
            {field.label}
          </label>
          <input
            type={field.type}
            name={field.name}
            className="w-full border rounded-lg p-2"
          />
          <p className={`${field.name === "email" ? "visible" : "invisible"}
            text-sm text-red-600`}>
            {field.error}
          </p>
        </div>
      ))}
      <button className="w-full bg-blue-600 text-white py-2 rounded-lg">
        Submit
      </button>
    </form>
  );
}

Loading State Manager

This component manages visibility of content during loading states.

This is a live editor. Play around with it!
export default function LoadingStateManager() {
  const contentItems = [
    { id: 1, title: "Latest Blog Post", content: "Understanding React Hooks", loading: true },
    { id: 2, title: "Featured Product", content: "Wireless Earbuds", loading: false },
    { id: 3, title: "User Statistics", content: "Monthly Active Users: 10k", loading: true },
    { id: 4, title: "System Status", content: "All Systems Operational", loading: false },
    { id: 5, title: "Recent Orders", content: "15 New Orders Today", loading: true },
    { id: 6, title: "Support Tickets", content: "5 Open Tickets", loading: false }
  ];

  return (
    <div className="grid grid-cols-2 gap-4 p-6">
      {contentItems.map((item) => (
        <div key={item.id} className="border rounded-lg p-4">
          <h3 className="text-lg font-bold">{item.title}</h3>
          <div className={`${item.loading ? "invisible" : "visible"}`}>
            <p className="text-gray-600">{item.content}</p>
          </div>
          <div className={`${item.loading ? "visible" : "invisible"}
            animate-pulse bg-gray-200 h-4 w-3/4 rounded mt-2`}>
          </div>
        </div>
      ))}
    </div>
  );
}

Best Practices

Maintain Design Consistency

When utilizing Tailwind CSS’ visibility utilities, it is essential to maintain a consistent design across the project. Consistent visual styles ensure a better user experience, irrespective of device or screen size.

Consider a card layout in which certain sections appear interactively. By ensuring consistent transitions and states, such as hiding descriptions on smaller screens and revealing them on hover or breakpoint-specific conditions, you can create dynamic design interfaces.

Optimize for Reusability

A reusable design system is a cornerstone of efficient development, and Tailwind CSS utilities, including visibility classes, play a vital role in this process.

Grouping frequently used combinations of visibility states and other utilities into reusable components minimizes redundancy throughout the project. This ensures that the same reusable component can cater to multiple contexts, significantly reducing development time while improving scalability.

Accessibility Considerations

Enhance Readability and Navigability

Visibility utilities directly impact the readability and navigability of your content. By using visible and invisible effectively, you can guide users’ attention to key areas without overwhelming them with unnecessary information. For instance, a step-by-step tutorial might reveal content progressively, using visibility classes to hide future steps until users complete the current one.

When toggling visibility, use animations or transitions to provide visual feedback. Smooth transitions between visible and invisible states help users understand the flow of content and maintain focus.

Support Accessible Interactive Elements

Interactive components such as modals, tooltips, dropdowns, and menus often rely on visibility utilities to manage their display states. Tailwind's visible and invisible classes, when combined with focus or hover states, allow developers to create accessible, dynamic interactions.

For example, a dropdown menu might default to invisible but become visible when the user hovers over or focuses on its parent element. This ensures the dropdown is accessible to both mouse users and keyboard users relying on the tab key for navigation.

Debugging Common Issues

Resolve Common Problems

Common issues with visibility utilities arise from unintended conflicts with other layout or spacing properties.

For instance, combining invisible with hidden in a component may lead to confusion, as both attempt to hide the same element differently. Review your class structure to eliminate redundant or conflicting utilities to address this.

Handle Nested Element Challenges

Handling visibility within nested structures, such as deeply nested navigation components or modals, often leads to cascading unintended behaviors.

For instance, parent containers marked invisible misapply their state to nested child elements unintentionally. To solve this, set visibility states directly on targeted elements or use utility combinations selectively in nested structures.