Examples

Article Cards

Mixed-height editorial cards using measured shortest-column placement.

Editorial cards often have unpredictable text length. MasonryBalanced measures the rendered card and places the next item in the shortest column.

Performance7 min read

Designing feeds that still feel fast at 10,000 items

A practical guide to windowing, measurement, and when placeholders improve the scroll experience.

Images4 min read

Treat image dimensions as layout data

Known width and height values let the layout settle before media finishes loading.

Accessibility5 min read

Masonry grids can still be semantic

Source order, list roles, and item metadata matter more than the visual column a card lands in.

Responsive6 min read

Container breakpoints beat viewport assumptions

Use breakpoint maps when the component lives inside panels, sidebars, or resizable workspaces.

Design8 min read

Composing card systems around unknown height

Small differences in copy, badges, and media ratios add up. Balanced placement keeps the page calm.

Example

import { clsx } from 'clsx';
import { MasonryBalanced } from 'masonix';

type Article = {
  id: string;
  title: string;
  section: string;
  excerpt: string;
  readTime: string;
};

function ArticleCard({ article }: { article: Article }) {
  return (
    <article
      className={clsx(
        'min-w-0',
        'p-5',
        'rounded-lg border',
        'border-zinc-200/80 bg-white text-zinc-950 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50',
      )}
    >
      <div
        className={clsx(
          'flex items-center justify-between gap-3',
          'pb-4',
          'border-b',
          'border-zinc-200/70 dark:border-zinc-800',
        )}
      >
        <span
          className={clsx(
            'font-mono text-[11px] font-medium uppercase tracking-[0.14em]',
            'text-zinc-500 dark:text-zinc-400',
          )}
        >
          {article.section}
        </span>
        <span className={clsx('text-xs', 'text-zinc-500 dark:text-zinc-400')}>
          {article.readTime} read
        </span>
      </div>
      <h3 className={clsx('mt-4', 'text-base font-semibold leading-6')}>
        {article.title}
      </h3>
      <p
        className={clsx(
          'mt-3',
          'text-sm leading-6',
          'text-zinc-600 dark:text-zinc-400',
        )}
      >
        {article.excerpt}
      </p>
    </article>
  );
}

export function ArticleGrid({ articles }: { articles: Article[] }) {
  return (
    <MasonryBalanced
      items={articles}
      columns={{ 0: 1, 640: 2 }}
      gap={16}
      estimatedItemHeight={220}
      itemKey={(article) => article.id}
      render={({ data }) => <ArticleCard article={data} />}
    />
  );
}

When to use

Use this for blogs, resource hubs, changelogs, case studies, and dashboard cards with optional sections.

Notes

  • Pick an estimatedItemHeight near the average rendered card.
  • Use stable keys so measured heights follow the correct article.
  • Use minItemHeight if loading states can briefly measure too small.

On this page