Skip to content

Components

TL;DR. Components are namespaced keywords with a Malli schema. Each platform registers its own renderer for that keyword. :wun/Stack and :myapp/Card go through the same path — framework code has no special privilege.

A component is a namespaced keyword with a per-platform renderer. :wun/Stack, :wun/Text, :wun/Button are the foundational set the framework ships; :myapp/Card, :acme/RichEditor are user-namespace components that flow through the same registry.

defcomponent

(defcomponent :myapp/Card
{:since 1
:schema [:map [:title {:optional true} :string]]
:loading :inherit
:fallback :web
:ios "Card"
:android "Card"})
keymeaning
:sinceFirst version of the component. Bump on breaking schema changes.
:schemaMalli schema for the props map.
:loading:inherit / :shimmer / :none — what to show while data loads.
:fallback:web (default) collapses unsupported clients to a :wun/WebFrame.
:iosDisplay name for the iOS native renderer.
:androidDisplay name for the Android native renderer.

The runtime doesn’t validate :since, :loading, etc. — they’re metadata for capability negotiation and tooling.

Per-platform renderers

Each platform has its own registry. The framework ships WunFoundation for the :wun/* set; user code registers its own components the same way. Pick your platform — the choice persists across the site via syncKey="platform", so a Swift reader stays on Swift everywhere.

(ns myapp.web.renderers
(:require [wun.web.renderers :as r]))
(r/register! :myapp/Card
(fn [{:keys [title]} children]
(into [:div.myapp-card
(when title [:h3 title])]
children)))

Capability negotiation

When a client connects it advertises the components it can render (X-Wun-Capabilities header, or ?caps= for EventSource). The server runs wun.capabilities/substitute over the rendered tree and replaces unsupported components with [:wun/WebFrame ...] at the smallest containing level — so an unsupported :myapp/RichEditor inside a :wun/Stack becomes a single WebFrame, not the whole stack.

The :wun/WebFrame renderer points at a server-rendered HTML stub showing the component’s actual content. Native clients open it in a WKWebView (iOS) or WebView (Android).

See it live

Terminal window
wun status

Prints a per-component coverage matrix:

component web ios android server-html
:wun/Stack ✓ ✓ ✓ ✓
:wun/Avatar ◌ ✓ ✓ ✓
:myapp/Greeting ◌ ✓ ✓ ·

✓ native renderer present · ◌ WebFrame fallback · · server default HTML.