Skip to content
ilinxa/pro-ui

Info List

alphav0.2.0

Card-framed list of icon-prefixed rows — primary and secondary text, optional actions, per-row links, comfortable and compact variants.

Category: Data DisplayUpdated: 2026-08-11Created: 2026-05-02Author: ilinxa

Context

Use for sidebar info cards on detail pages — Event Details, Contact, Address, Account Settings, Product Specs, Restaurant Info, Listing Attributes. Comfortable variant: stacked primary/secondary/action rows with separators (kasder Etkinlik Bilgileri pattern). Compact variant: inline single-line rows, no separators (kasder İletişim pattern). Migration origin: kasder kas-social-front-v0 events/[id]/page.tsx Etkinlik Bilgileri + İletişim sidebar cards.

Installation

Initialize shadcn (once per project)Seeds lib/utils.ts and components.json. Skip if you've already used any shadcn component.
pnpm dlx shadcn@latest init
Register the @ilinxa namespace (once per project)Add to your components.json. Merge with existing config.
"registries": {
  "@ilinxa": "https://ui.ilinxa.com/r/{name}.json"
}
Install the component
pnpm dlx shadcn@latest add @ilinxa/info-list

Add -fixtures for dummy data:

pnpm dlx shadcn@latest add @ilinxa/info-list-fixtures

Preview

Etkinlik Bilgileri

  • 31 Mayıs 2026 Pazar

    09:00 - 18:00

  • İstanbul Kongre Merkezi

    Darülbedai Caddesi No:3, 34367 Harbiye/Şişli/İstanbul

    Haritada Gör
  • 500 Kişilik Kapasite

    423 kişi kayıtlı

Demo source

demo.tsxtsx

Usage

When to use

Reach for InfoList for sidebar info cards on detail pages — Event Details, Contact Info, Address, Account Settings, Product Specs, Restaurant Info, Listing Attributes. Two visual flavors: comfortable (stacked primary / secondary / action with separators between rows) and compact(inline single-line rows, no separators).

Minimal example

import { InfoList } from "@/components/info-list";
import { Calendar, MapPin, Users } from "lucide-react";

<InfoList
  heading="Event Details"
  variant="comfortable"
  items={[
    { id: "date", icon: Calendar, primary: "May 31, 2026", secondary: "09:00 - 18:00" },
    { id: "loc",  icon: MapPin,   primary: "Istanbul Conference Center", secondary: "Şişli/Istanbul" },
    { id: "cap",  icon: Users,    primary: "500-person capacity", secondary: "423 registered" },
  ]}
/>;

Two variants

  • comfortable — stacked content (primary bold + secondary muted + optional action). Icon w-5 h-5 text-primary. Separators between rows by default. Use for sidebar info cards with rich per-row content.
  • compact — inline single-line rows. Icon w-4 h-4 text-muted-foreground. No separators by default. Use for contact lists, footer columns, dense info summaries. Compact ignores secondary and action — they imply a richer row than the variant supports.

Linked rows

Pass href per item to wrap primary in the polymorphic linkComponent. Consumer passes full URLs — no auto-prefixing for tel: / mailto:.

<InfoList
  variant="compact"
  items={[
    { id: "phone", icon: Phone, primary: "+90 (212) 555 0123", href: "tel:+902125550123" },
    { id: "email", icon: Mail,  primary: "info@example.com",   href: "mailto:info@example.com" },
  ]}
/>

Action slot

Comfortable rows can render an actionReactNode below the secondary line — typically a link button (e.g. "View on map"):

{
  id: "location",
  icon: MapPin,
  primary: event.location,
  secondary: event.address,
  action: (
    <Button variant="link" className="px-0 h-auto">
      View on map <ExternalLink className="w-3 h-3" />
    </Button>
  ),
}

Polymorphic linkComponent

import NextLink from "next/link";

<InfoList
  variant="compact"
  linkComponent={NextLink}
  items={[
    { id: "profile", icon: User, primary: "View profile", href: "/profile" },
  ]}
/>

Custom renderItem

Bypass the default row layout entirely with renderItem(item) — useful for swapping icon for an avatar, embedding a status badge, or any non-icon row anatomy:

<InfoList
  items={items}
  renderItem={(item) => (
    <div className="flex items-center gap-3">
      <Avatar src={people[item.id].image} />
      <div>
        <p className="font-medium">{item.primary}</p>
        <p className="text-sm text-muted-foreground">{item.secondary}</p>
      </div>
    </div>
  )}
/>

Frame + separators

  • framed: true (default) — card chrome (bg-card rounded-2xl p-6 border). Pass framed={false} for embedded use inside an existing card.
  • separated — auto-defaults per variant: comfortable=true, compact=false. CSS top-border on each row except the first; override by passing the prop.

Empty state

Empty items array → renders emptyState if provided, else <p role="status"> with labels.emptyText(default "No information.").

Notes

  • Renders <ul role="list">; section aria-labelledby resolves to the heading id (via useId) when heading is supplied.
  • Decorative icons are aria-hidden="true"; link's accessible name is the visible primary text.
  • Heading defaults to h3 (sidebar info cards typically nest under page h2). Bump via headingAs="h2" when standalone.

Features

  • Two variants — comfortable (stacked, separators, w-5 primary icon) and compact (inline, no separators, w-4 muted icon)
  • Required icon + primary; optional secondary + action + href per item
  • Polymorphic per-row link via linkComponent (default 'a') — works with tel: / mailto: / etc.
  • Frame toggle (framed=true card chrome / framed=false bare)
  • Separator toggle (auto-defaults per variant; overridable)
  • Custom renderItem slot for full row takeover
  • Optional section heading with configurable level (h2/h3/h4)
  • Soft-failure on optional fields
  • Empty state slot + labels.emptyText fallback
  • i18n via labels object
  • <ul role='list'> semantics + section aria-labelledby
  • Compact variant intentionally ignores secondary + action (documented limitation)

Tags

info-listdetailsinfolistsidebar

Dependencies

npm peer deps: lucide-react@^1.11.0