Latest posts and category archives. It receives posts as data (PostSummary) — any CMS maps into that shape; in stored pages an f.query field is resolved by the app's resolvers.posts. Each post is an article with one link on its title that covers the whole item (::after), dates are time[datetime], pagination is plain URLs in a labelled nav.
From the journal
Kitchen
How we plan a menu around the morning boats
Twelve years of buying fish at Kumkapı before sunrise, and why the menu is only printed at four.
6 min readBy Deniz Arslan
Installation
The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";
Usage
import { BlogList, BlogListSkeleton, type PostSummary } from "@vamos-ui/react/blocks";
const posts: PostSummary[] = await cms.posts({ limit: 6 }).then(toPostSummary);
<BlogList title="From the *journal*" posts={posts} allLink={{ href: "/journal", label: "All posts" }}
pagination={{ page, total, hrefTemplate: "/journal?page={page}" }} />
// Streaming
<Suspense fallback={<BlogListSkeleton />}><LatestPosts /></Suspense>Examples
List with categories and pagination
Date · title + excerpt · reading time rows. Categories are links with aria-current; page 1 drops ?page=1.
Journal
Notes from the kitchen
How we plan a menu around the morning boats
Twelve years of buying fish at Kumkapı before sunrise, and why the menu is only printed at four.
6 min read
Bonito season, week by week
Palamut in September, torik in October, and why lakerda waits until winter.
5 min read
4 min read
Grid (SaaS guides)
Three columns of 3:2 covers without card frames.
Guides
Run a calmer front desk
Empty state and skeleton
No posts: a message and an optional action (newsletter). BlogListSkeleton is the Suspense fallback.
Events
No events planned yet
Wine nights and fish suppers are announced here and by email, usually two weeks ahead.
API
BlogList
| Prop | Type | Default |
|---|---|---|
variantLayout. | "editorial" | "list" | "grid" | "compact" | "editorial" |
posts{ href, title, excerpt?, cover?, category?, publishedAt, readingMinutes?, author? }. | PostSummary[] | — |
categoriesCategory filter links. | { label; href; active? }[] | — |
paginationPrev / next links and “Page 2 of 5”. href() for code, hrefTemplate for JSON. | { page; total; href?(p) ; hrefTemplate? } | — |
allLink“All posts” beside the header. | { href; label } | — |
showCoversCovers in editorial and grid. | boolean | true |
emptyEmpty-state copy. | { title?; text?; action? } | — |
postHeadingLevelDefault 3 under a title, else 2. | 2 | 3 | 4 | — |
eyebrow / title / leadSection header. | ReactNode | — |
labels / locale“{count} min read”, pagination strings; dates via Intl. | Partial<Site2Labels> / string | — |
theme / anchor / id / className / headingLevel / components / editModeShared block props. | BlockBaseProps | — |
BlogListSkeleton
| Prop | Type | Default |
|---|---|---|
variant / countaria-hidden placeholder shapes. | BlogListVariant / number | "grid" / 3 |