React

How to build a blog index

A blog index is a list of posts with date, author, topic and reading time, built from an array. Here are the fields and the endpoint that feed it.

Key points

  • Five fields cover the index and the post page: slug, title, date, topic, reading time.
  • Sort by publication date, never by modification date.
  • All 8 blog blocks in the catalogue declare their licence as see source.

A blog index is a list of posts with date, author, topic and reading time, built from an array of data. It does not need a CMS: it needs stable fields and one sort order.

The minimum fields of one entry

Five fields cover both the list and the post page. Add more only after these five are mandatory.

  • Slug: the stable identifier; the title can change, the slug cannot.
  • Title and summary: two distinct texts, not one text cut in half.
  • ISO date and author: one format only, with the time zone declared.
  • Topic: a single label taken from a closed list.
  • Reading time: calculated from the word count, never typed by hand.

The command

The catalogue API returns the blog blocks as JSON, with a licence and dependencies per entry.

curl "https://ui.eneawork.it/api/blocks/category/blog?limit=10"

The index needs the same shape: a list sorted by date, with a limit and a cursor. Without a limit in the query, the page grows until the browser gives up.

A concrete example: the list

type Post = { slug: string; title: string; publishedAt: string; topic: string; minutes: number };

export function PostList({ posts }: { posts: Post[] }) {
  return (
    
      {posts.map((post) => (
        
          {post.title}
          {post.publishedAt.slice(0, 10)}
          {post.topic} · {post.minutes} min
        
      ))}
    
  );
}

The key is the slug, not the array index: when you filter or reorder, React reuses the right node. A time element with an ISO dateTime makes the date readable to a crawler, and the minutes arrive already computed from the file, not from the render. The list keeps no state: change the array, change the markup, and there is nothing left to synchronise.

On 1 November 2026 node scripts/validate-blog-quality.js counts 538 indexed articles on this site. At twelve entries per page that needs 45 pages: the division is arithmetic, not an estimate, and it explains why the page number belongs in the URL rather than in component state.

Three records to inspect

The Cnippet blogs block, the Shadcnblocks blog7 and the blog category show three different densities: a compact list, an image grid, a list with an author.

Order, pagination, empty state

Three decisions to make before writing the component: the order (publication date), the pagination (a fixed number per page) and the empty state (a message, not an empty grid).

The limitation

Eight blocks from two sources declare their licence as see source: the catalogue cannot tell you whether you may use them in a commercial product. None of the eight solves pagination either: you write that yourself.

The decision rule

One date decides the order: publishedAt. Fixing a typo moves nothing; rewriting an article and wanting it on top means changing publishedAt and leaving the slug alone, so the URL and outside links keep working. The modification date stays on the post page, outside the sort.

The mistake to avoid

Calculating reading time on raw HTML: tags, attributes and code blocks inflate the count, and two articles of equal length get different times. Strip the tags, count the words and divide by 200 once, when the file is saved; recompute it in three components and the three values will diverge.

For the cursor use the slug of the last article, not an offset: an article published while the reader scrolls shifts every position and page two shows a duplicate. The slug stays stable even when the order changes, and the query becomes ?after=slug.

Where to continue

Sections for a magazine are in blocks for blog and magazine; the structured data for a post is in how to add schema markup; the verified shortlist is in best blog blocks 2026.

Frequently asked questions

Which fields does a blog index need?

Slug, title, summary, ISO date, author, topic and reading time. Those seven fields build both the list and the post page.

Does a blog index need a CMS?

No. An array of objects in a JSON file holds up to a few hundred posts. A CMS earns its place when several people write without touching the repository.

How should I order posts in the index?

By publication date, newest first. Ordering by modification date pushes old posts to the top every time you fix a comma.

How many posts per page?

Ten to twenty. Below ten, pagination is pointless; above twenty, load time shows on mobile.

How do I calculate reading time?

Divide the word count by 200 and round up. It is an estimate, but it stays consistent across posts because one calculation produces it.