Server-Driven UI Pages

Idea

Define a structured API response format that drives native UI rendering across all clients. The server controls what content appears and in what order. The app controls how each component looks. New content, products, articles, and pages ship without app store updates.

Motivation

  • App store updates are slow (Apple review 1-3 days) and disruptive for content-only changes
  • T.REX ARMS product pages (targets, gear, drill kits) need to be updated frequently without app releases
  • Church security education content, announcements, and tips should be publishable on the server’s schedule
  • White-label deployments need their own content pages — Springfield PD shows department content, not T.REX promotions
  • Apple and Google explicitly allow server-driven UI where the app renders native components from structured data (as opposed to downloading and executing code, which is prohibited)

How It Works

The server returns a structured response describing a page as an ordered list of typed sections. The app has a fixed vocabulary of section types it knows how to render natively (SwiftUI, Jetpack Compose, Dioxus). Unknown section types are skipped gracefully for forward compatibility.

Example response:

{
  "page_id": "trex-store",
  "title": "T.REX ARMS Store",
  "sections": [
    {
      "type": "hero_banner",
      "image_url": "/assets/trex-banner.jpg",
      "title": "Training Essentials",
      "subtitle": "Everything you need for your next range day"
    },
    {
      "type": "product_grid",
      "items": [
        {
          "title": "IDPA Target Pack (50ct)",
          "image_url": "/assets/idpa-targets.jpg",
          "price": "$24.99",
          "external_url": "https://trexarms.com/targets/idpa-50"
        }
      ]
    },
    {
      "type": "markdown_block",
      "body": "### Why standardized targets matter\nUsing official kit means your scores are directly comparable..."
    },
    {
      "type": "drill_carousel",
      "title": "Popular This Week",
      "drill_ids": ["0193a500-...", "0193a500-..."]
    },
    {
      "type": "cta_button",
      "label": "Browse All Drills",
      "action": "navigate",
      "destination": "/drills"
    }
  ]
}

Section Type Vocabulary

A finite set of building blocks the app knows how to render:

Section typeRenders as
hero_bannerFull-width image with title/subtitle overlay
markdown_blockRich text (reuses the markdown renderer needed for drill descriptions)
product_gridGrid of cards with image, title, price, external link
drill_carouselHorizontal scroll of drill cards, taps navigate to drill detail
cta_buttonProminent button with in-app navigation or external link
video_embedEmbedded video player (YouTube/Vimeo URL)
image_gallerySwipeable image carousel
dividerVisual separator
announcementHighlighted text block for tips, alerts, or promos

Adding a new section type requires an app update. But new content using existing types is purely server-side.

Use Cases

  • T.REX product pages — targets, drill kits, gear tied to specific drills (“buy the kit for this drill”)
  • Church security education — articles, checklists, planning guides
  • Announcements — new features, beta invites, promotions
  • Onboarding content — role-specific guides (church security, LE, instructor)
  • White-label custom content — each deployment serves its own pages via the same API
  • Seasonal/topical content — holiday schedules, training tips, safety reminders

What Not to Build

  • Not a generic layout engine (no flexbox, no grid positioning, no arbitrary nesting)
  • No remote styling (fonts, colors, spacing are controlled by the app’s design system)
  • No executable code from the server (this would violate app store policies)
  • No per-server section type versioning — the app knows what it can render, unknown types are skipped

Relationship to Content Feed API

The content-feed-api roadmap item covers server-driven announcements and content. This idea is the rendering layer for that — defining how the app renders server-driven content. The content feed API could serve pages in this format, or a separate “pages” endpoint could exist alongside it. Either way, the section vocabulary and rendering logic are shared.

Open Questions

  • Should pages be cacheable with a TTL, or always fetched fresh?
  • Is there a page editor on the dashboard, or are pages authored as JSON/YAML on the server?
  • Should sections support conditional visibility (e.g., show only to certain roles or subscription tiers)?
  • How does this interact with the drill kits idea — is a drill kit page a server-driven page, or its own first-class entity?
  • Localization — can section content be localized, or is that a post-MVP concern?