Menu

Tailwind CSS Columns

Columns allow you to split the content of an element into multiple columns, similar to how text is formatted in newspapers or magazines. This is particularly useful when designing layouts that need to display content in an organized and readable fashion.

Tailwind CSS provides a comprehensive set of utilities to control and customize column properties. With Tailwind's utilities, you can quickly manage column count, width, gaps, and responsive behaviors. Let's delve into the specifics.

ClassPropertiesExample
columns-1columns: 1;<div className="columns-1"></div>
columns-2columns: 2;<div className="columns-2"></div>
columns-3columns: 3;<div className="columns-3"></div>
columns-4columns: 4;<div className="columns-4"></div>
columns-5columns: 5;<div className="columns-5"></div>
columns-6columns: 6;<div className="columns-6"></div>
columns-7columns: 7;<div className="columns-7"></div>
columns-8columns: 8;<div className="columns-8"></div>
columns-9columns: 9;<div className="columns-9"></div>
columns-10columns: 10;<div className="columns-10"></div>
columns-11columns: 11;<div className="columns-11"></div>
columns-12columns: 12;<div className="columns-12"></div>
columns-autocolumns: auto;<div className="columns-auto"></div>
columns-3xscolumns: 16rem; <div className="columns-3xs"></div>
columns-2xscolumns: 18rem; <div className="columns-2xs"></div>
columns-xscolumns: 20rem; <div className="columns-xs"></div>
columns-smcolumns: 24rem; <div className="columns-sm"></div>
columns-mdcolumns: 28rem; <div className="columns-md"></div>
columns-lgcolumns: 32rem; <div className="columns-lg"></div>
columns-xlcolumns: 36rem; <div className="columns-xl"></div>
columns-2xlcolumns: 42rem; <div className="columns-2xl"></div>
columns-3xlcolumns: 48rem; <div className="columns-3xl"></div>
columns-4xlcolumns: 56rem; <div className="columns-4xl"></div>
columns-5xlcolumns: 64rem; <div className="columns-5xl"></div>
columns-6xlcolumns: 72rem; <div className="columns-6xl"></div>
columns-7xlcolumns: 80rem; <div className="columns-7xl"></div>

Overview of Columns

Columns based on Count

To specify the number of columns based on count- use utilities like columns-3, columns-4, etc. Each column's width will be automatically calculated based on the total columns.

This is a live editor. Play around with it!
export default function App() {
  return (
    <div className="columns-3 h-screen w-screen bg-gray-50 p-5">
      {/* columns-3: CSS equivalent (columns: 3) */}
      <img src="https://images.unsplash.com/photo-1488998427799-e3362cec87c3" alt="Random" className="mb-4" />
      <p>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut faucibus, mauris quis vehicula
        accumsan, augue risus luctus nulla, nec convallis tortor augue non felis.
      </p>
    </div>
  );
}

Columns based on Width

To specify each column's width instead of number, use utilities like- columns-xs, columns-sm, columns-md, etc. The total column number will be automatically calculated based on the width and available space.

The below example has two columns of 16rem each.

This is a live editor. Play around with it!
export default function App() {
  return (
    <div className="columns-3xs w-[550px] gap-6 h-screen w-screen bg-gray-100 p-4">
      {/* columns-3xs: CSS equivalent (columns: 16rem) */}
      <img src="https://images.unsplash.com/photo-1488998427799-e3362cec87c3" alt="Random" className="mb-6" />
      <p>
        Sed vitae orci ullamcorper, volutpat lorem auctor, maximus nisl. Donec augue ante,
        ultricies et efficitur sed, gravida a dui.
      </p>
    </div>
  );
}

Adding the Column Gap

To define the gap between each column, use the gap-* utilities, e.g., gap-2, gap-4, gap-8, etc.

This is a live editor. Play around with it!
export default function App() {
  return (
    <div className="columns-4 gap-8 h-screen w-screen bg-white p-6">
      {/* columns-4: CSS equivalent (columns: 4) */}
      {/* gap-8: CSS equivalent (column-gap: 2rem) */}
      <p>
        Proin ac arcu vel risus pharetra sollicitudin non et libero. Praesent tempus dignissim urna,
        nec molestie lacus volutpat at.
      </p>
      <img src="https://images.unsplash.com/photo-1488998427799-e3362cec87c3" alt="Random Image" />
    </div>
  );
}

States and Responsiveness

Column behaviors can be adjusted conditionally for states such as hover or focus, as well as across breakpoints for responsiveness. These utilities ensure your layouts remain adaptable across various devices and states.

Hover and Focus States

You can modify columns when an element's state changes (e.g., upon hover or focus). Tailwind offers hover and focus modifiers to adjust column count, width, or gap conditionally.

This is a live editor. Play around with it!
export default function App() {
  return (
    <div tabindex="0" className="columns-2 hover:columns-4 focus:columns-5 gap-5 p-5 h-screen w-screen bg-blue-50">
      {/* columns-2: Default */}
      {/* hover:columns-4: On hover, increase the column count */}
      {/* focus:columns-5: On focus, further increase the column count */}
      <p>Hover or focus to increase the number of columns.</p>
      <img src="https://images.unsplash.com/photo-1488998427799-e3362cec87c3" alt="Demo" />
    </div>
  );
}

Breakpoint Modifiers

Responsive designs demand layouts that adapt fluidly to screen size. Tailwind's responsive modifiers- sm, md, lg, etc. ensure column utilities apply only on certain breakpoints.

This is a live editor. Play around with it!
export default function App() {
  return (
    <div className="columns-1 sm:columns-2 lg:columns-4 gap-4 bg-gray-200 p-6 h-screen w-screen">
      {/* columns-1: Default for small devices */}
      {/* sm:columns-2: Medium breakpoint */}
      {/* lg:columns-4: Large breakpoint */}
      <p>Resize the screen to observe changes in the column structure.</p>
      <img src="https://images.unsplash.com/photo-1488998427799-e3362cec87c3" alt="Dynamic Layout" />
    </div>
  );
}

Custom Columns

When predefined utilities are not enough, you can use custom values to define the number of width of the columns. This includes extending the theme or using arbitrary values.

Extending the Theme

Extending Tailwind's default configuration allows you to add custom column utilities globally (e.g., unique column counts or gaps). Open your project's tailwind.config.js file and extend the theme object to define new utilities. Once done, you can apply these utilities in your components.

This is a live editor. Play around with it!
import tailwindConfig from "./tailwind.config.js";
tailwind.config = tailwindConfig;

export default function App() {
  return (
    <div className="columns-tiny p-6 bg-yellow-50 h-screen w-screen">
      {/* columns-tiny: 13 custom 100px columns */}
      <img src="https://images.unsplash.com/photo-1488998427799-e3362cec87c3" alt="Extended Theme" />
      <p>Custom column configurations tailored to your project's design needs.</p>
    </div>
  );
}

Using Arbitrary Values

For one-off situations where extending the theme is too much effort, you can apply arbitrary values directly in your component.

This is a live editor. Play around with it!
export default function App() {
  return (
    <div className="columns-[50px] gap-8 bg-gray-50 p-4 h-screen w-screen">
      {/* columns-[50px]: One-time custom column width */}
      <p>
        Use arbitrary columns for unique layouts without modifying the global theme.
      </p>
      <img src="https://images.unsplash.com/photo-1488998427799-e3362cec87c3" alt="Custom Columns" />
    </div>
  );
}

Real World Examples

Product Feature Grid with Alternating Columns

A responsive layout showcasing product features with icons and descriptions that switches between 2-4 columns based on screen size.

This is a live editor. Play around with it!
export default function ProductFeatures() {
  const features = [
    {
      icon: "https://images.unsplash.com/photo-1611162617474-5b21e879e113",
      title: "Cloud Storage",
      description: "Secure and scalable storage solutions for your enterprise"
    },
    {
      icon: "https://images.unsplash.com/photo-1551288049-bebda4e38f71",
      title: "Analytics Dashboard",
      description: "Real-time insights and data visualization tools"
    },
    {
      icon: "https://images.unsplash.com/photo-1633356122102-3fe601e05bd2",
      title: "API Integration",
      description: "Seamless connectivity with third-party services"
    },
    {
      icon: "https://images.unsplash.com/photo-1633356122544-f134324a6cee",
      title: "Multi-factor Auth",
      description: "Enhanced security for your applications"
    },
    {
      icon: "https://images.unsplash.com/photo-1633356122102-3fe601e05bd2",
      title: "Automated Backups",
      description: "Scheduled backups with instant recovery options"
    },
    {
      icon: "https://images.unsplash.com/photo-1633356122544-f134324a6cee",
      title: "24/7 Support",
      description: "Round-the-clock technical assistance"
    }
  ];

  return (
    <div className="container mx-auto px-4 py-12">
      <div className="columns-2 sm:columns-3 lg:columns-4 gap-6">
        {features.map((feature, index) => (
          <div key={index} className="break-inside-avoid mb-6 p-6 bg-white rounded-xl shadow-lg">
            <img 
              src={feature.icon} 
              alt={feature.title}
              className="w-12 h-12 rounded-lg mb-4"
            />
            <h3 className="text-xl font-bold mb-2">{feature.title}</h3>
            <p className="text-gray-600">{feature.description}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

Blog Article Masonry Layout

A masonry-style blog layout with varying content lengths organized in responsive columns.

This is a live editor. Play around with it!
export default function BlogMasonry() {
  const articles = [
    {
      image: "https://images.unsplash.com/photo-1661956601349-f61c959a8fd4",
      title: "The Future of Web Development",
      excerpt: "Exploring upcoming trends in web technologies and development practices",
      category: "Tech",
      readTime: "5 min"
    },
    {
      image: "https://images.unsplash.com/photo-1661956601030-fdfb9c7e9e2f",
      title: "Designing for Accessibility",
      excerpt: "Best practices for creating inclusive web experiences",
      category: "Design",
      readTime: "8 min"
    },
    {
      image: "https://images.unsplash.com/photo-1661956602116-aa6865609028",
      title: "Performance Optimization",
      excerpt: "Tips and tricks for boosting website performance",
      category: "Dev",
      readTime: "6 min"
    },
    {
      image: "https://images.unsplash.com/photo-1661956602153-23384936a1d3",
      title: "Mobile-First Development",
      excerpt: "Building responsive websites for modern devices",
      category: "Mobile",
      readTime: "7 min"
    },
    {
      image: "https://images.unsplash.com/photo-1558986377-c44f6a2b50f0",
      title: "CSS Architecture",
      excerpt: "Structuring stylesheets for maintainability",
      category: "CSS",
      readTime: "4 min"
    },
    {
      image: "https://images.unsplash.com/photo-1672309558498-cfcc89afff25",
      title: "Modern JavaScript",
      excerpt: "Latest features and best practices in JavaScript",
      category: "JS",
      readTime: "9 min"
    }
  ];

  return (
    <div className="container mx-auto px-4 py-12">
      <div className="columns-2 lg:columns-3 gap-6">
        {articles.map((article, index) => (
          <div key={index} className="break-inside-avoid mb-6 bg-white rounded-2xl overflow-hidden shadow-lg hover:shadow-xl transition-shadow duration-300">
            <img 
              src={article.image} 
              alt={article.title}
              className="w-full h-48 object-cover rounded-t-2xl"
            />
            <div className="p-5">
              <div className="flex justify-between items-center mb-3">
                <span className="bg-blue-100 text-blue-800 px-3 py-1 rounded-full text-xs font-medium">
                  {article.category}
                </span>
                <span className="text-gray-500 text-sm">{article.readTime}</span>
              </div>
              <h3 className="text-lg font-bold text-gray-900">{article.title}</h3>
              <p className="text-gray-600 mt-1 text-sm">{article.excerpt}</p>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

A responsive portfolio gallery with image masonry layout and hover effects.

This is a live editor. Play around with it!
export default function PortfolioGallery() {
  const projects = [
    {
      image: "https://images.unsplash.com/photo-1661956601349-f61c959a8fd4",
      title: "E-commerce Platform",
      category: "Web Dev",
      description: "Custom shopping platform with advanced features"
    },
    {
      image: "https://images.unsplash.com/photo-1661956601030-fdfb9c7e9e2f",
      title: "Mobile Banking App",
      category: "Design",
      description: "Intuitive financial management application"
    },
    {
      image: "https://images.unsplash.com/photo-1661956602116-aa6865609028",
      title: "Social Media Dashboard",
      category: "Analytics",
      description: "Comprehensive social media management tool"
    },
    {
      image: "https://images.unsplash.com/photo-1661956602153-23384936a1d3",
      title: "Healthcare Portal",
      category: "Web App",
      description: "Patient management system"
    },
    {
      image: "https://images.unsplash.com/photo-1595675024853-0f3ec9098ac7",
      title: "Real Estate Platform",
      category: "Full Stack",
      description: "Property listing and management system"
    },
    {
      image: "https://images.unsplash.com/photo-1583755391669-5d4e2d1cf3d9",
      title: "Learning Management",
      category: "EdTech",
      description: "Online education platform"
    }
  ];

  return (
    <div className="container mx-auto px-4 py-12 bg-gray-100">
      <div className="columns-2">
        {projects.map((project, index) => (
          <div key={index} className="relative group overflow-hidden rounded-lg shadow-lg mb-4">
            {/* Background Image */}
            <div className="relative w-full h-48">
              <img 
                src={project.image} 
                alt={project.title}
                className="absolute inset-0 w-full h-full object-cover transform transition-transform duration-500 group-hover:scale-105"
              />
            </div>
            
            <div className="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-80 transition-all duration-500 flex items-center justify-center p-3">
              <div className="text-white text-center opacity-0 group-hover:opacity-100 transition-opacity duration-500">
                <h3 className="text-lg font-bold">{project.title}</h3>
                <span className="inline-block bg-white/20 px-3 py-1 rounded-full text-xs uppercase tracking-wider mt-2">
                  {project.category}
                </span>
                <p className="text-xs mt-2">{project.description}</p>
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

Team Member Directory

A responsive team directory with member cards organized in columns.

This is a live editor. Play around with it!
export default function TeamDirectory() {
  const team = [
    {
      image: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80",
      name: "Sarah Johnson",
      role: "CEO & Founder",
      bio: "10+ years of startup experience",
      social: {
        twitter: "#",
        linkedin: "#",
        github: "#"
      }
    },
    {
      image: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e",
      name: "Michael Chen",
      role: "Lead Developer",
      bio: "Full-stack expert with ML background",
      social: {
        twitter: "#",
        linkedin: "#",
        github: "#"
      }
    },
    {
      image: "https://images.unsplash.com/photo-1517841905240-472988babdf9",
      name: "Emma Williams",
      role: "UX Designer",
      bio: "Specializes in user research",
      social: {
        twitter: "#",
        linkedin: "#",
        github: "#"
      }
    },
    {
      image: "https://images.unsplash.com/photo-1463453091185-61582044d556",
      name: "James Wilson",
      role: "Product Manager",
      bio: "Former Google PM",
      social: {
        twitter: "#",
        linkedin: "#",
        github: "#"
      }
    },
    {
      image: "https://images.unsplash.com/photo-1539571696357-5a69c17a67c6",
      name: "Alex Rodriguez",
      role: "Marketing Lead",
      bio: "Digital marketing specialist",
      social: {
        twitter: "#",
        linkedin: "#",
        github: "#"
      }
    },
    {
      image: "https://images.unsplash.com/photo-1494790108377-be9c29b29330",
      name: "Lisa Chang",
      role: "Backend Engineer",
      bio: "Cloud infrastructure expert",
      social: {
        twitter: "#",
        linkedin: "#",
        github: "#"
      }
    }
  ];

  return (
    <div className="container mx-auto px-4 py-12">
      <div className="columns-2 lg:columns-3 xl:columns-4 gap-6">
        {team.map((member, index) => (
          <div key={index} className="break-inside-avoid mb-6">
            <div className="bg-white rounded-xl shadow-lg overflow-hidden">
              <img 
                src={member.image} 
                alt={member.name}
                className="w-full h-64 object-cover"
              />
              <div className="p-6">
                <h3 className="text-xl font-bold mb-1">{member.name}</h3>
                <p className="text-blue-600 font-medium mb-2">{member.role}</p>
                <p className="text-gray-600 mb-4">{member.bio}</p>
                <div className="flex space-x-4">
                  <a href={member.social.twitter} className="text-gray-400 hover:text-blue-500">
                    <svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
                      <path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"/>
                    </svg>
                  </a>
                  <a href={member.social.linkedin} className="text-gray-400 hover:text-blue-700">
                    <svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
                      <path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z"/>
                    </svg>
                  </a>
                  <a href={member.social.github} className="text-gray-400 hover:text-gray-900">
                    <svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
                      <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
                    </svg>
                  </a>
                </div>
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

News Feed Timeline

A dynamic news feed with alternating column layout and timeline indicators.

This is a live editor. Play around with it!
export default function NewsFeed() {
  const newsItems = [
    {
      image: "https://images.unsplash.com/photo-1661956601349-f61c959a8fd4",
      title: "AI Tech Revolution",
      timestamp: "2 hours ago",
      category: "Technology",
      content: "Revolutionary advancement in artificial intelligence promises new possibilities"
    },
    {
      image: "https://images.unsplash.com/photo-1661956601030-fdfb9c7e9e2f",
      title: "Space Exploration Update",
      timestamp: "4 hours ago",
      category: "Science",
      content: "NASA announces new mission to explore distant galaxies"
    },
    {
      image: "https://images.unsplash.com/photo-1661956602116-aa6865609028",
      title: "Climate Change Report",
      timestamp: "6 hours ago",
      category: "Environment",
      content: "Latest findings on global climate patterns reveal concerning trends"
    },
    {
      image: "https://images.unsplash.com/photo-1661956602153-23384936a1d3",
      title: "Medical Research Discovery",
      timestamp: "8 hours ago",
      category: "Health",
      content: "Breakthrough in cancer treatment shows promising results"
    },
  ];

  return (
    <div className="container mx-auto px-4 py-12">
      <div className="columns-2 md:columns-3 gap-8">
        {newsItems.map((item, index) => (
          <div key={index} className="break-inside-avoid mb-8 relative">
            <div className="bg-white rounded-xl shadow-lg overflow-hidden">
              <div className="relative">
                <img 
                  src={item.image} 
                  alt={item.title}
                  className="w-full h-48 object-cover"
                />
                <div className="absolute top-4 right-4 bg-white px-3 py-1 rounded-full text-sm font-medium">
                  {item.category}
                </div>
              </div>
              <div className="p-6">
                <div className="flex items-center text-gray-500 text-sm mb-3">
                  <svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
                  </svg>
                  {item.timestamp}
                </div>
                <h3 className="text-xl font-bold mb-3">{item.title}</h3>
                <p className="text-gray-600">{item.content}</p>
                <button className="mt-4 text-blue-600 font-medium hover:text-blue-800 transition-colors">
                  Read more →
                </button>
              </div>
            </div>
            <div className="absolute top-0 left-0 w-1 h-full bg-gradient-to-b from-blue-500 to-purple-500 rounded-full" />
          </div>
        ))}
      </div>
    </div>
  );
}

Customization Examples

Blog Grid

A flexible blog grid using custom column width utilities to adapt across screen size.

This is a live editor. Play around with it!
import tailwindConfig from "./tailwind.config.js";
tailwind.config = tailwindConfig;

// App.js
import React from 'react';

const BlogGrid = () => {
  const posts = [
    { title: 'Mountain Adventures', image: 'https://images.unsplash.com/photo-1464822759023-fed622ff2c3b' },
    { title: 'Ocean Vibes', image: 'https://images.unsplash.com/photo-1505118380757-91f5f5632de0' },
    { title: 'Forest Tales', image: 'https://images.unsplash.com/photo-1511497584788-876760111969' },
    { title: 'Desert Stories', image: 'https://images.unsplash.com/photo-1509316785289-025f5b846b35' }
  ];

  return (
    <div className="p-4 columns-narrow md:columns-blog lg:columns-wide space-y-4">
      {posts.map((post, index) => (
        <div key={index} className="break-inside-avoid mb-4 rounded-lg overflow-hidden shadow-lg">
          <img src={post.image} alt={post.title} className="w-full h-48 object-cover" />
          <div className="p-4 bg-white">
            <h3 className="text-lg font-semibold">{post.title}</h3>
            <p className="text-gray-600 mt-2">Discover the beauty of nature.</p>
          </div>
        </div>
      ))}
    </div>
  );
};

export default BlogGrid;

Masonry Photo Grid

A dynamic masonry-style photo grid layout with custom column width.

This is a live editor. Play around with it!
import tailwindConfig from "./tailwind.config.js";
tailwind.config = tailwindConfig;

// MasonryGrid.js
import React from 'react';

const MasonryGrid = () => {
  return (
    <div className="columns-masonry gap-4 p-4 space-y-4">
      <img src="https://images.unsplash.com/photo-1558979158-65a1eaa08691" alt="Landscape" className="w-full rounded-lg shadow-lg break-inside-avoid" />
      <img src="https://images.unsplash.com/photo-1461988320302-91bde64fc8e4" alt="Sunset" className="w-full rounded-lg shadow-lg break-inside-avoid" />
      <img src="https://images.unsplash.com/photo-1524781289445-ddf8f5695861" alt="Valley" className="w-full rounded-lg shadow-lg break-inside-avoid" />
      <img src="https://images.unsplash.com/photo-1526779259212-939e64788e3c" alt="Forest" className="w-full rounded-lg shadow-lg break-inside-avoid" />
      <img src="https://images.unsplash.com/photo-1493246507139-91e8fad9978e" alt="Ocean" className="w-full rounded-lg shadow-lg break-inside-avoid" />
      <img src="https://images.unsplash.com/photo-1518098268026-4e89f1a2cd8e" alt="Desert" className="w-full rounded-lg shadow-lg break-inside-avoid" />
      <img src="https://images.unsplash.com/photo-1518173946687-a4c8892bbd9f" alt="Lake" className="w-full rounded-lg shadow-lg break-inside-avoid" />
      <img src="https://images.unsplash.com/photo-1503614472-8c93d56e92ce" alt="Canyon" className="w-full rounded-lg shadow-lg break-inside-avoid" />
      <img src="https://images.unsplash.com/photo-1505144808419-1957a94ca61e" alt="Waterfall" className="w-full rounded-lg shadow-lg break-inside-avoid" />
    </div>
  );
};

export default MasonryGrid;

Minimal Art Collection

A clean, minimalist layout for art collections with custom column width.

This is a live editor. Play around with it!
import tailwindConfig from "./tailwind.config.js";
tailwind.config = tailwindConfig;

const ArtCollection = () => {
  const artworks = [
    "https://images.unsplash.com/photo-1550784343-6bd0ce5d600b",
    "https://images.unsplash.com/photo-1547826039-bfc35e0f1ea8",
    "https://images.unsplash.com/photo-1547891654-e66ed7ebb968",
];

  return (
    <div className="bg-white p-2">
      <div className="columns-art gap-2 space-y-2">
        {artworks.map((url, index) => (
          <div 
            key={index} 
            className="break-inside-avoid group relative"
          >
            <img 
              src={url} 
              alt={`Artwork ${index + 1}`}
              className="w-full rounded-sm hover:shadow-xl transition-shadow duration-300 h-screen object-cover"
            />
            <div className="absolute inset-0 bg-white/10 opacity-0 group-hover:opacity-100 transition-opacity duration-200"></div>
          </div>
        ))}
      </div>
    </div>
  );
};

export default ArtCollection;

Best Practices

Maintain Design Consistency

To maintain consistency, define clear guidelines on how and when to use columns-* utilities in your code. Establishing fixed column widths for content sections, such as columns-2 or columns-3, ensures that similar sections have a harmonious appearance across different pages.

Additionally, define project-specific columns utilities for frequently used column structures to enhance maintainability. Standardized column classes ensure that the same grid logic applies across multiple components, reducing unnecessary styling variations and enforcing a more predictable design language.

Build Responsive Design

Tailwind’s responsive modifiers (sm:, md:, lg:, etc.) allow for dynamic column adjustments based on screen sizes. You can define a fixed number of columns using sm:columns-2 md:columns-3 lg:columns-4, etc. Alternatively, you can specify column width such as sm:columns-sm md:columns-md lg:columns-lg, etc. so the number of columns adjust automatically based on available space.

Regardless of the approach, thorough testing is crucial to ensure the layout behaves as expected across various devices and resolutions. Elements may shift unexpectedly, especially with dynamic widths, so checking for readability, spacing, and accessibility issues is essential.

Accessibility Considerations

Enhance Readability and Navigability

Column-based layouts should prioritize readability and ease of navigation. Ensure that text flows naturally across columns preventing cognitive overload for users. Overly narrow columns with small text sizes can be difficult to read, while excessively wide columns may cause strain when tracking from one line to the next.

Use appropriate text size utilities to improve readability within multi-column setups. Additionally, ensure sufficient line height (leading-relaxed) to enhance text clarity and reduce visual clutter.

When designing interactive elements within columns, ensure sufficient spacing and contrast. Provide clear visual separation between interactive and non-interactive elements using borders, outlines, or shadows to help users differentiate between actionable components and static content.

Ensure Keyboard Accessibility

Keyboard navigation should work seamlessly when implementing column-based layouts.To maintain a smooth navigation flow, ensure tabindex is properly assigned to important non-clickable elements like <div>, <section>, or <span>, allowing them to be included in the focus order when necessary. This helps users who rely on keyboards move through the content logically without skipping over critical sections.

Additionally, apply clear focus indicators to improve accessibility by making interactive elements easier to identify. Use Tailwind’s focus:outline-* and focus:ring-* utilities to ensure that buttons, inputs, and links remain visible when selected via the keyboard. Since column layouts can sometimes feel disconnected when tabbing through elements, these visual cues help users stay oriented.