PATTERN Cited by 1 source
HTML subset to native UI mapping¶
Problem¶
You want one UI codebase that renders on web + iOS + Android. Two directions are available:
- Native primitives → HTML adapter (react-native-web):
write RN components (
<View />,<ScrollView />), let a library translate to HTML at runtime. Carries runtime adapter cost on web. - HTML subset → native primitives adapter
(react-strict-dom): write HTML subset (
html.div,html.button), let a library map to RN on mobile and pass-through on web. No runtime cost on web because a build step strips the adapter.
Pattern (the second direction)¶
Write UI in an HTML + CSS subset (html.div,
html.button, html.span), paired with
StyleX for styling. Let
react-strict-dom handle the
mapping:
- Web: passes through to native DOM elements. Build step strips the abstraction. Zero runtime cost.
- Native (iOS/Android): maps to RN primitives
(
<Text>,<View>) at build/runtime.
import { css, html } from 'react-strict-dom';
const styles = css.create({
button: {
backgroundColor: { default: 'white', ':hover': 'lightgray' },
padding: 10
}
});
function MyButton() {
return (
<html.button style={styles.button}>
A cross-platform button
</html.button>
);
}
Why the HTML-subset direction wins here¶
Two arguments, both from Zalando's 2025-10 post:
- Substrate longevity. HTML + CSS have had multiple decades of specification and implementation work compounding behind them. RN's component model is younger and more volatile. Zalando: "HTML and CSS are incredibly expressive, have evolved over many years, and are likely to remain relevant for years to come. In contrast, any other form of UI representation could potentially become obsolete at any point."
- Zero runtime cost on web. The build step eliminates the abstraction layers on web; output is plain HTML + CSS with no adapter overhead. react-native-web carries that adapter at runtime.
Structural constraints¶
The subset constraint from concepts/cross-platform-ui-subset-tradeoff applies: react-strict-dom's HTML surface covers only the intersection of what all platforms can render. For everything outside:
- Compat API escape hatch —
patterns/compat-native-escape-hatch
(
compat.nativefor passing platform-specific props to the mapped native component). - Platform-specific file resolution —
patterns/platform-specific-ts-file-resolution
(
Foo.native.ts,Foo.ts).
Both are necessary for a realistic cross-platform component library.
When to reach for this pattern¶
- You're building cross-platform UI (web + iOS + Android) from one codebase.
- You want web output to be plain HTML/CSS — no JS adapter.
- You're OK with accepting HTML subset as the expressive ceiling, plus a disciplined escape-hatch story for the non-intersection.
When not to¶
- Your target is mobile-only (no web). The HTML framing adds complexity for no benefit; use RN directly.
- Your app is very native-forward (heavy use of platform-specific navigation primitives, camera integration, hardware sensors). The HTML subset covers less of your surface; the subset-to-escape-hatch ratio is bad.
Seen in¶
- sources/2025-10-02-zalando-accelerating-mobile-app-development-with-rendering-engine-and-react-native — canonical wiki first source. Zalando uses this pattern with StyleX tokens (ZDS tokens, systems/zalando-design-system-tokens) for cross-platform theming.