mdxui

Rendering System

How Things become Components at every level

Rendering System

The core insight of mdxui: Any Thing can render at any level.

Thing (schema.org.ai)
  ×
Level (Site, Page, Section, Panel, etc.)
  ×
Display (cards, rows, tiles, etc.)
  =
Component

The Hierarchy

There are exactly three structural levels in each context:

LevelSite ContextApp ContextPurpose
1SiteAppRoot container, theme, providers
2PageViewFull-screen route, URL-addressable
3SectionPanelRegion within a page/view

Below Section/Panel, structure gives way to display modes.


Display Modes

A Section or Panel contains collections of Things. The display mode determines how those Things are laid out:

Grid Displays

DisplayDescriptionBest For
cardsCard grid, responsive columnsProducts, People, Projects
tilesImage-heavy grid, masonry-stylePhotos, Portfolio, Gallery
bentoMixed-size grid cellsFeatures, Highlights

List Displays

DisplayDescriptionBest For
listVertical stackArticles, Notifications
rowsTable rows with columnsData, Records, Admin
feedSocial-style feedPosts, Activity
timelineChronological with datesEvents, History, Changelog

Spatial Displays

DisplayDescriptionBest For
mapGeographic markersLocations, Directories
calendarDate gridEvents, Scheduling
kanbanColumns/swim lanesTasks, Pipeline, Workflow
treeHierarchical nodesFiles, Org charts, Categories

Interactive Displays

DisplayDescriptionBest For
carouselHorizontal scrollFeatured, Testimonials
galleryLightbox with navigationImages, Media
tabsTabbed contentCategories, Variants
accordionCollapsible sectionsFAQ, Settings

Thing Renderers

Each Thing type can have a renderer for each display mode:

// The renderer naming convention
type RendererName = `${ThingType}${DisplayMode}`

// Examples:
PersonCard      // Person in cards display
PersonRow       // Person in rows display
PersonMarker    // Person on map display
ProductCard     // Product in cards display
ProductTile     // Product in tiles display
EventCard       // Event in cards display
EventCalendarItem // Event in calendar display
AgentCard       // Agent in cards display
AgentPanel      // Agent as a Panel (in App)

Renderer Inheritance

Renderers inherit up the schema.org type hierarchy:

Thing (base)
├── Person
│   ├── Patient
│   └── PerformingGroup → MusicGroup
├── Organization
│   ├── Corporation
│   ├── LocalBusiness → Restaurant
│   └── SportsTeam
├── Product
│   ├── Vehicle → Car
│   └── IndividualProduct
├── CreativeWork
│   ├── Article → BlogPosting
│   ├── SoftwareApplication
│   └── WebSite
├── Event
│   ├── BusinessEvent
│   └── MusicEvent
└── [AI Extensions]
    ├── Agent
    ├── Tool
    ├── Model
    └── Prompt

Lookup order for rendering a Car as a card:

1. CarCard          // Most specific
2. VehicleCard      // Parent type
3. ProductCard      // Grandparent
4. ThingCard        // Ultimate fallback

The ThingRenderer Interface

Every display mode requires a base Thing renderer:

interface ThingRenderers {
  // Grid displays
  ThingCard: Component<ThingProps>
  ThingTile: Component<ThingProps>
  ThingBento: Component<ThingProps>

  // List displays
  ThingListItem: Component<ThingProps>
  ThingRow: Component<ThingProps>
  ThingFeedItem: Component<ThingProps>
  ThingTimelineItem: Component<ThingProps>

  // Spatial displays
  ThingMarker: Component<ThingProps>
  ThingCalendarItem: Component<ThingProps>
  ThingKanbanCard: Component<ThingProps>
  ThingTreeNode: Component<ThingProps>

  // Interactive displays
  ThingCarouselSlide: Component<ThingProps>
  ThingGalleryItem: Component<ThingProps>
  ThingTab: Component<ThingProps>
  ThingAccordionItem: Component<ThingProps>
}

ThingProps

Every Thing renderer receives standardized props:

interface ThingProps<T extends Thing = Thing> {
  // The Thing data (from schema.org.ai)
  $type: string
  $id?: string

  // Common Thing properties
  name?: string
  description?: string
  image?: string | ImageObject
  url?: string

  // Type-specific properties spread in
  ...T

  // Rendering context
  display: DisplayMode
  level: Level
  index?: number

  // Interaction callbacks
  onClick?: () => void
  onSelect?: (selected: boolean) => void
  href?: string
}

Collections

A Collection is a set of Things with a display mode:

// In MDX
<Collection
  of="Product"
  display="cards"
  columns={3}
  from="/api/products"
/>

// Renders as
<div className="grid grid-cols-3 gap-4">
  <ProductCard {...product1} />
  <ProductCard {...product2} />
  <ProductCard {...product3} />
</div>

Collection Props

interface CollectionProps<T extends Thing = Thing> {
  // What to render
  of: string              // Thing type: "Product", "Person", etc.
  things?: T[]            // Direct data
  from?: string           // API endpoint to fetch from

  // How to display
  display: DisplayMode    // "cards", "rows", "tiles", etc.

  // Layout options (vary by display)
  columns?: number        // For grid displays
  gap?: number | string

  // Filtering & sorting
  filter?: (thing: T) => boolean
  sort?: keyof T | ((a: T, b: T) => number)
  limit?: number

  // Empty & loading states
  empty?: ReactNode
  loading?: ReactNode

  // Item customization
  renderItem?: (thing: T, index: number) => ReactNode
}

Display Mode Components

Each display mode is also a component:

// These are equivalent:
<Collection of="Product" display="cards" />
<Cards of="Product" />

// Display-specific components
<Cards of="Product" columns={3} />
<Rows of="Product" columns={['name', 'price', 'status']} />
<Tiles of="Product" />
<List of="Product" />
<Map of="LocalBusiness" />
<Calendar of="Event" />
<Kanban of="Task" columns={['todo', 'doing', 'done']} />
<Timeline of="Event" />
<Feed of="BlogPosting" />
<Gallery of="ImageObject" />
<Carousel of="Testimonial" />
<Tree of="Category" />

Putting It Together

A typical Site page:

<Page>
  <Section>
    <Hero {...hero} />
  </Section>

  <Section title="Features">
    <Bento of="Feature" />
  </Section>

  <Section title="Testimonials">
    <Carousel of="Testimonial" />
  </Section>

  <Section title="Team">
    <Cards of="Person" columns={4} />
  </Section>

  <Section title="FAQ">
    <Accordion of="Question" />
  </Section>
</Page>

A typical App view:

<View>
  <Panel position="sidebar">
    <Tree of="Category" />
  </Panel>

  <Panel position="main">
    <Rows
      of="Product"
      columns={['name', 'category', 'price', 'status']}
      selectable
    />
  </Panel>

  <Panel position="detail">
    <ProductPanel {...selectedProduct} />
  </Panel>
</View>

Next: Schema.org Type Mappings

Each schema.org type has sensible defaults for:

  1. Which display modes it supports
  2. Which properties to show in each mode
  3. Default sorting and filtering

See Type Mappings for the complete reference.

On this page