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.
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
estimatedItemHeightnear the average rendered card. - Use stable keys so measured heights follow the correct article.
- Use
minItemHeightif loading states can briefly measure too small.