Renderer model
motif renders to real DOM on the web and to real React Native on native. There is no shared component implementation underneath — the two renderers meet only at the API surface and the token system.
Status
Accepted.
Context
A cross-platform styling library has to decide where web and native converge. Three shapes were on the table:
- One implementation, one renderer. Build for web, paper React Native on top with a compatibility shim — or the reverse.
- Two implementations, shared API. Web has its own real-DOM components; native has its own React Native components; the shared layer is the API and the token system, nothing below it.
- A framework-agnostic abstraction that neither platform sees directly.
Option 1 always sacrifices one platform — the papered-over side inherits the host's quirks and none of its strengths. Option 3 adds an indirection layer that every component pays for and no user asked for.
Decision
motif takes option 2 — two trees, one shared API.
- The web renderer (
@usemotif/react) is real DOM and real CSS. A<Box>is a<div>; styles are real stylesheet rules and CSS variables. - The native renderer (
@usemotif/react-native) is real React Native. A<Box>is a<View>; styles areStyleSheetentries. - What the two share is the API surface — the style-prop schema,
styled(), the token model — not the component code. Platform selection is the bundler's job, resolved through thereact-nativeandbrowserpackage-export conditions and.native.tsfile extensions.
motif is React-centric by the same decision: it ships React components, not a framework-agnostic core. Desktop is a web shell — Electron or Tauri — for v1; the option-2 architecture leaves a real desktop renderer open as a later addition without disturbing the other two.
Consequences
- Each platform gets idiomatic output. Web users get cascade, media queries, and
:has; native users getStyleSheetand the platform's own layout engine. Neither is emulated. - The cost is two implementations to maintain. A primitive is not done until both renderers ship it, and a behaviour change has to land in both.
- The shared API is a hard contract. The conformance suite exists precisely to prove the two renderers resolve the same props to the same styles — without it, "shared API" would be an aspiration rather than a guarantee.