Article Meta
alphav0.2.0Icon-and-value metadata strip for article headers — author, date, read time, view count, or any custom pairs.
Context
Reach for it on any long-form content surface: news articles, blog post headers, doc page bylines, video player meta lines, podcast episode headers, forum thread titles, GitHub-issue-style metadata strips. Data-driven items array of arbitrary length and shape. Optional per-item href (with polymorphic linkComponent) makes individual items clickable (byline → author page, date → permalink). Optional bottom divider for the standard 'between hero and body' placement.
Installation
pnpm dlx shadcn@latest initpnpm dlx shadcn@latest add @ilinxa/article-metaAdd -fixtures for dummy data:
pnpm dlx shadcn@latest add @ilinxa/article-meta-fixturesCLI can't resolve @ilinxa? The namespace is listed in the official shadcn registry directory, so current CLIs need no configuration. If yours can't resolve it (older or pinned versions, self-hosted mirrors), register it manually in components.json:
"registries": {
"@ilinxa": "https://ui.ilinxa.com/r/{name}.json"
}Preview
How sustainable cities are rethinking density
- Maya Chen
- Apr 28, 2026
- 5 min read
- 12.4k
Lead paragraph would go here. The meta strip cleanly separates the headline from the body via the bottom rule.
Demo source
"use client"; import { Tabs, TabsContent, TabsTrigger } from "@/components/ui/tabs";import { SwipeTabsList } from "@/components/site/swipe-tabs-list";import { ArticleMeta } from "./article-meta";import { ARTICLE_META_DUMMY, ARTICLE_META_DUMMY_CENTERED, ARTICLE_META_DUMMY_DOCS, ARTICLE_META_DUMMY_TEXT_ONLY, ARTICLE_META_DUMMY_VIDEO,} from "./dummy-data"; export default function ArticleMetaDemo() { return ( <Tabs defaultValue="default" className="w-full"> <SwipeTabsList> <TabsTrigger value="default">Default</TabsTrigger> <TabsTrigger value="centered">Centered</TabsTrigger> <TabsTrigger value="end">End-aligned</TabsTrigger> <TabsTrigger value="clickable">Clickable byline</TabsTrigger> <TabsTrigger value="text-only">Text-only</TabsTrigger> <TabsTrigger value="video">Video meta</TabsTrigger> </SwipeTabsList> <TabsContent value="default" className="mt-6 max-w-2xl"> <h2 className="text-2xl font-serif font-bold text-foreground mb-4"> How sustainable cities are rethinking density </h2> <ArticleMeta items={ARTICLE_META_DUMMY} divider /> <p className="mt-6 text-sm text-muted-foreground"> Lead paragraph would go here. The meta strip cleanly separates the headline from the body via the bottom rule. </p> </TabsContent> <TabsContent value="centered" className="mt-6 max-w-2xl"> <h2 className="text-2xl font-serif font-bold text-foreground text-center mb-4"> Public transit on the rebound </h2> <ArticleMeta items={ARTICLE_META_DUMMY_CENTERED} align="center" /> </TabsContent> <TabsContent value="end" className="mt-6 max-w-2xl"> <h2 className="text-2xl font-serif font-bold text-foreground text-right mb-4"> Sidebar dispatches: the data this week </h2> <ArticleMeta items={ARTICLE_META_DUMMY} align="end" /> <p className="mt-3 text-xs text-muted-foreground"> <code>align="end"</code> right-aligns the meta strip — typical for sidebars where the headline is right-aligned, or for RTL layouts where the visual gravity should match the reading direction. </p> </TabsContent> <TabsContent value="clickable" className="mt-6 max-w-2xl"> <ArticleMeta items={ARTICLE_META_DUMMY_DOCS} /> <p className="mt-3 text-xs text-muted-foreground"> Hover the byline (<code>@maya</code>) — color shifts on the clickable item; focus-visible ring shows on keyboard nav. </p> </TabsContent> <TabsContent value="text-only" className="mt-6 max-w-2xl"> <ArticleMeta items={ARTICLE_META_DUMMY_TEXT_ONLY} /> <p className="mt-3 text-xs text-muted-foreground"> Items can omit <code>icon</code> entirely — pure label-value strip. </p> </TabsContent> <TabsContent value="video" className="mt-6 max-w-2xl"> <ArticleMeta items={ARTICLE_META_DUMMY_VIDEO} gapClass="gap-3" /> <p className="mt-3 text-xs text-muted-foreground"> Tighter gap (<code>gap-3</code>) for video-player-style meta lines. </p> </TabsContent> </Tabs> );} Usage
When to use
A horizontal strip of icon + value pairs that surface key metadata about a piece of content immediately under its title or hero. Reach for it on news article pages, blog post headers, doc page bylines, video player meta lines, podcast episode headers, forum thread titles — anywhere long-form content needs a quick metadata summary.
Basic example
import { Calendar, Clock, Eye, User } from "lucide-react"
import { ArticleMeta } from "@/components/article-meta"
export function ArticleHeader() {
return (
<ArticleMeta
divider
items={[
{ id: "author", icon: User, value: "Maya Chen", href: "/team/maya-chen" },
{ id: "date", icon: Calendar, value: "Apr 28, 2026" },
{ id: "read", icon: Clock, value: "5 min read", ariaLabel: "5 minute read" },
{ id: "views", icon: Eye, value: "12.4k", ariaLabel: "12,400 views" },
]}
/>
)
}Per-item link
Each item accepts an optional href — usually for the byline (link to author page) or date (link to permalink / archive). Pass linkComponent for router-aware links.
import Link from "next/link"
<ArticleMeta
items={[
{ id: "author", icon: User, value: "Maya Chen", href: "/team/maya-chen" },
{ id: "date", icon: Calendar, value: "Apr 28, 2026", href: "/archive/2026-04-28" },
]}
linkComponent={Link}
/>Layout knobs
align—start(default) /center/end. Usecenterfor centered meta lines under hero overlays.divider—trueappliespb-8 border-b border-borderfor the "between hero and body" placement.gapClass— defaults togap-6. Override for tighter rhythms (e.g.gap-3for video player meta).
Notes
- Items render as
<li>inside a<ul role="list">for free list semantics (the explicit role works around Safari's VoiceOver handling oflist-style: none). - Item icons are decorative (
aria-hidden). For icon-meaning clarity in screen readers, setariaLabelon the item — it composes the link's accessible name whenhrefis set, or wraps the unlinked content in a labeled span. - Items can omit
iconfor text-only strips (e.g. category / issue / page metadata). - Values are pre-formatted
ReactNode. Bring your own date formatter (Intl.DateTimeFormat,date-fns, etc.) and number formatter (Intl.NumberFormat). - The component is exported as
React.memo. Memoize theitemsarray (or pass a stable reference) for the memo to bite — inlineitems={[...]}defeats it.
Features
- Data-driven items array (icon + value + optional href + optional ariaLabel)
- Polymorphic per-item link via linkComponent (default native anchor)
- Optional bottom divider (`pb-8 border-b border-border`)
- 3 alignment modes (start / center / end)
- Tunable gap class (default `gap-6`; override for tighter rhythms)
- Items can omit `icon` for text-only strips
- Memoized; SSR-safe; <ul>/<li> semantics with Safari workaround