@groooh/react-native-webify
v0.1.1
Published
Zero-runtime build-time compiler and universal design system converting React Native components into semantic React Web & Next.js code
Maintainers
Readme
@groooh/react-native-webify
Write once in React Native using design tokens. Compile to 100% semantic, accessible, zero-runtime React & Next.js web code.
Created by d-naum • Powered by Groooh
🌐 Live Web Demo: https://react-native-webify-boilerplate.vercel.app/
📱 Starter & Examples Boilerplate: https://github.com/d-naum/react-native-webify-boilerplate
🔗 Live Demo & Starter Kit
Experience the zero-runtime compiled components live in your browser:
- 🌐 Production Web Demo: https://react-native-webify-boilerplate.vercel.app/
- 📦 GitHub Starter Boilerplate: https://github.com/d-naum/react-native-webify-boilerplate
- 📖 Component & Event API Reference: docs/COMPONENTS_REFERENCE.md
⚡ Why @groooh/react-native-webify?
Traditional solutions like react-native-web inject a heavy runtime compatibility layer, turning every native component into nested <div> and <span style="display:flex"> tags. This creates deep, unsemantic DOM trees, degrades web performance, harms SEO, and causes hydration penalties in modern SSR frameworks like Next.js.
@groooh/react-native-webify solves this at build time.
Using a high-performance TypeScript AST (Abstract Syntax Tree) compiler, it reads your React Native source code, statically evaluates your design tokens, and transforms native primitives into native HTML5 elements (<article>, <h1>-<h6>, <button>, <input>, <dialog>, <progress>, etc.).
┌──────────────────────────────────────────────┐
│ React Native Component + Tokens │
│ <Card padding="lg"> │
│ <Heading variant="h2">Title</Heading> │
│ <Button variant="primary">Submit</Button> │
│ </Card> │
└──────────────────────┬───────────────────────┘
│
┌─────────────┴─────────────┐
▼ ▼
┌─────────────────┐ ┌────────────────────────────────────────────────────────┐
│ iOS / Android │ │ React Web / Next.js (0 KB Runtime Overhead) │
│ Native Runtime │ │ <article style={{ padding: "24px", ... }}> │
│ View, Text, │ │ <h2>Title</h2> │
│ Pressable │ │ <button style={{ backgroundColor: "#6366f1" }}> │
│ │ │ Submit │
│ │ │ </button> │
│ │ │ </article> │
└─────────────────┘ └────────────────────────────────────────────────────────┘Key Differentiators
- 0 KB Web Runtime Overhead: No polyfill bundle, no runtime translation cost, no style injection shims.
- Semantic HTML5 Output: Renders native
<article>,<section>,<nav>,<input>,<button>,<dialog>,<progress>,<hr>,<figure>, and<details>. - First-Class Next.js Support: Compatible with React 19, Next.js App Router (Server Components & Client Components), and static exports.
- Centralized Design Token Engine: Spacing, colors, typography, borders, and breakpoints compile directly into static inline styles.
- Lightning-Fast CLI: Compiles directories in milliseconds and includes an active
--watchmode for hot development.
📦 Installation
In your React Native / Expo Mobile Project
# npm
npm install @groooh/react-native-webify
# yarn
yarn add @groooh/react-native-webify
# pnpm
pnpm add @groooh/react-native-webifyIn your React Web / Next.js Project (Build Compiler & CLI)
# npm
npm install -D @groooh/react-native-webify
# yarn
yarn add -D @groooh/react-native-webify
# pnpm
pnpm add -D @groooh/react-native-webify🚀 Quick Start
1. Write your component in React Native
Create your shared cross-platform component using @groooh/react-native-webify primitives and design tokens:
// src/components/UserProfileCard.tsx
import React from "react";
import {
Card,
Row,
Stack,
Avatar,
Heading,
Body,
Badge,
Button,
Divider,
} from "@groooh/react-native-webify";
export interface UserProfileCardProps {
name: string;
role: string;
avatarUrl?: string;
isOnline?: boolean;
onFollow?: () => void;
}
export const UserProfileCard = ({
name,
role,
avatarUrl,
isOnline = true,
onFollow,
}: UserProfileCardProps) => {
return (
<Card padding="lg">
<Stack gap="md">
<Row align="center" justify="between">
<Row align="center" gap="sm">
<Avatar name={name} uri={avatarUrl} size="md" />
<Stack gap="xs">
<Body weight="bold">{name}</Body>
<Body size="xs" color="neutral.500">{role}</Body>
</Stack>
</Row>
<Badge
variant={isOnline ? "success" : "neutral"}
label={isOnline ? "Active" : "Offline"}
/>
</Row>
<Divider />
<Heading variant="h3">Profile Overview</Heading>
<Body size="sm" color="neutral.600">
Cross-platform engineer building high-performance universal user interfaces.
</Body>
<Row justify="end" gap="sm">
<Button variant="secondary" size="sm">Message</Button>
<Button variant="primary" size="sm" onPress={onFollow}>Follow</Button>
</Row>
</Stack>
</Card>
);
};2. Compile to Semantic React Web JSX
Run the compiler CLI to transform mobile components into web-native code:
# Single build
npx webify build -i src/components -o web/src/generated
# Or watch mode for instantaneous recompilation
npx webify build -i src/components -o web/src/generated --watch3. See the Generated Zero-Runtime Web Output
The compiler produces a clean, accessible .web.tsx file with zero native dependencies:
// web/src/generated/UserProfileCard.web.tsx
import * as React from "react";
export const UserProfileCard = ({ name, role, avatarUrl, isOnline = true, onFollow }) => {
return (
<article style={{ backgroundColor: "#ffffff", borderRadius: "12px", border: "1px solid #e5e7eb", padding: "24px" }}>
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
<div style={{ display: "flex", flexDirection: "row", alignItems: "center", justifyContent: "space-between" }}>
<div style={{ display: "flex", flexDirection: "row", alignItems: "center", gap: "8px" }}>
<figure style={{ display: "inline-flex", alignItems: "center", justifyContent: "center", borderRadius: "9999px", width: "40px", height: "40px" }} />
<div style={{ display: "flex", flexDirection: "column", gap: "4px" }}>
<p style={{ fontWeight: 700, margin: 0 }}>{name}</p>
<p style={{ fontSize: "12px", color: "#6b7280", margin: 0 }}>{role}</p>
</div>
</div>
<span style={{ backgroundColor: isOnline ? "#dcfce7" : "#f3f4f6", borderRadius: "9999px", padding: "2px 8px" }}>
{isOnline ? "Active" : "Offline"}
</span>
</div>
<hr style={{ border: "none", borderTop: "1px solid #e5e7eb", margin: 0 }} />
<h3 style={{ fontSize: "20px", fontWeight: 600, margin: 0 }}>Profile Overview</h3>
<p style={{ fontSize: "14px", color: "#4b5563", margin: 0 }}>
Cross-platform engineer building high-performance universal user interfaces.
</p>
<div style={{ display: "flex", flexDirection: "row", justifyContent: "flex-end", gap: "8px" }}>
<button style={{ backgroundColor: "#f3f4f6", color: "#111827", borderRadius: "8px", cursor: "pointer" }}>
Message
</button>
<button onClick={onFollow} style={{ backgroundColor: "#6366f1", color: "#ffffff", borderRadius: "8px", cursor: "pointer" }}>
Follow
</button>
</div>
</div>
</article>
);
};🎨 Design Token System
@groooh/react-native-webify includes a battle-tested design token system. Tokens can be passed directly as component props or consumed via @groooh/react-native-webify/tokens.
1. Semantic Color Palette
Each color family contains 10 carefully calibrated tints (50 through 900):
| Color Scale | Purpose | Values |
|---|---|---|
| primary | Main brand accents, primary buttons, active tabs | primary.50 to primary.900 (#6366f1 base) |
| neutral | Backgrounds, cards, text hierarchy, borders | neutral.50 to neutral.900 (#111827 base) |
| success | Confirmation states, completed indicators, tags | success.50 to success.900 (#22c55e base) |
| warning | Caution banners, pending items, highlights | warning.50 to warning.900 (#f59e0b base) |
| error | Destructive actions, validation errors, alerts | error.50 to error.900 (#ef4444 base) |
| info | System notices, informative tips | info.50 to info.900 (#0ea5e9 base) |
<Body color="neutral.700">High contrast body copy</Body>
<Card backgroundColor="neutral.50" borderColor="primary.200">...</Card>
<Badge variant="error" label="Failed" />2. Spacing Tokens
Consistent 4px/8px rhythm across both mobile layout engines and CSS:
| Token | Pixels | CSS Web Value | Usage |
|---|---|---|---|
| xs | 4px | "4px" | Tight label margins, indicator spacing |
| sm | 8px | "8px" | Compact gaps, button internal padding |
| md | 16px | "16px" | Standard stack spacing, card paddings |
| lg | 24px | "24px" | Section spacing, container padding |
| xl | 32px | "32px" | Modal padding, major layout gaps |
| 2xl | 48px | "48px" | Screen gutters, section dividers |
| 3xl | 64px | "64px" | Landing page hero spacing |
| 4xl | 96px | "96px" | Large desktop display spacing |
<Stack gap="lg" padding="xl">
<Row gap="sm">...</Row>
</Stack>3. Border Radius Tokens
| Token | Pixels | Equivalent |
|---|---|---|
| none | 0px | Squared |
| xs | 2px | Subtle corner rounding |
| sm | 4px | Small badges, code blocks |
| md | 8px | Standard buttons, input fields |
| lg | 12px | Cards, dialog modals |
| xl | 16px | Floating sheets, banners |
| full | 9999px | Pill badges, circular avatars |
<Card radius="lg">...</Card>
<Image radius="md" uri="..." />4. Typography Tokens
| Token | Font Size | Line Height | Native Mobile | Web Tag |
|---|---|---|---|---|
| h1 | 32px | 40px | <Text style={{ fontSize: 32 }}> | <h1> |
| h2 | 24px | 32px | <Text style={{ fontSize: 24 }}> | <h2> |
| h3 | 20px | 28px | <Text style={{ fontSize: 20 }}> | <h3> |
| h4 | 18px | 24px | <Text style={{ fontSize: 18 }}> | <h4> |
| h5 | 16px | 22px | <Text style={{ fontSize: 16 }}> | <h5> |
| h6 | 14px | 20px | <Text style={{ fontSize: 14 }}> | <h6> |
| body (md) | 16px | 24px | <Text style={{ fontSize: 16 }}> | <p> |
| caption | 12px | 16px | <Text style={{ fontSize: 12 }}> | <span> |
🧩 Complete Component Catalog (30+ Components)
| Category | Component | Mobile Representation | Web Compilation Output |
|---|---|---|---|
| Layout | Stack | <View style={{ flexDirection: 'column', gap }}> | <div style="display:flex; flex-direction:column; gap:..."> |
| | Row | <View style={{ flexDirection: 'row', gap, alignItems }}> | <div style="display:flex; flex-direction:row; gap:..."> |
| | Box / View | <View> | <div> |
| | ScrollView | <ScrollView> | <div style="overflow:auto"> |
| | FlatList | <FlatList> | Native .map() loop with item keys |
| Typography| Heading | <Text style={headingStyles[variant]}> | <h1>, <h2>, <h3>, <h4>, <h5>, <h6> |
| | Body | <Text style={bodyStyles[size]}> | <p style="..."> |
| | Caption | <Text style={captionStyles}> | <span style="..."> |
| Actions | Button | <Pressable style={buttonStyles}> | <button style="..."> (with onClick) |
| Form Inputs | Input | <TextInput> | <input type="text"> |
| | PasswordInput| <TextInput secureTextEntry> | <input type="password"> |
| | SearchInput | <TextInput> with search icon | <input type="search"> |
| | Textarea | <TextInput multiline> | <textarea> |
| | Slider | Gesture-driven track & thumb | <input type="range" min max value onChange> |
| | Checkbox | Pressable box with checkmark | <input type="checkbox"> |
| | Radio / RadioGroup | Pressable radio indicator group | <fieldset> + <input type="radio"> |
| | Select | Dropdown picker modal | <select> + <option> (styled 42px height & primary hover) |
| | DatePicker | Primary-themed calendar picker | <input type="date"> (42px height, inherited font & label) |
| | OTPInput | Discrete PIN digit boxes | Segmented <input maxLength="1"> series |
| | InputFrame | Label, error message, helper container | <div style="..."> container with field helper |
| Data Display| Card | <View style={cardStyles}> | <article style="..."> |
| | Badge | Pill container with colored status | <span style="..."> |
| | Tag | Removable chip with dismiss icon | <span style="..."> with close <button> |
| | Avatar | Profile photo / name initials / edit badge | <figure style="..."> with optional upload camera badge |
| | AvatarGroup | Overlapping avatar stack (+N badge) | <div style="display:flex"> overlapping stack |
| | Divider | Horizontal or vertical separator | <hr style="..."> |
| | Accordion | Animated collapsible disclosure | <details> + <summary> |
| | Tabs | Segmented tab button bar | Tab navigation with ARIA roles |
| | ProgressBar | Animated progress fill bar | <progress max="100" value="..."> |
| | Skeleton | Shimmer placeholder loader | <div style="background:linear-gradient..."> |
| Media & Upload | Image | <Image source={{ uri }}> | <img src="..." alt="..." /> |
| | ImageSlider | Multi-image carousel with dots & arrows | <div role="region"> with image slider & indicators |
| | FileUpload | Drag-and-drop file upload zone | Dropzone with SVG cloud upload icon & file preview |
| Overlays | Modal | Full-screen animated overlay | <dialog style="..."> |
| | BottomSheet | Drag-up mobile drawer sheet | Accessible overlay drawer container |
| | Drawer | Side sliding navigation sheet | <aside style="..."> |
| Feedback | Alert | Banner with warning/info icon | <div role="alert"> |
| | Spinner | Animated activity indicator | <div role="status"> SVG spinner |
| | Toast | Floating transient feedback notification | Floating notification banner |
| E-Commerce | ProductCard | Product card with multi-image slider | <article> with image carousel, price, rating, wishlist & cart |
| | Price | Formatted price with discount comparison | <div style="..."> with formatted price & <del> |
| | Rating | Star ratings (0-5) with review counts | <div role="img"> with stars & review count |
| | QuantitySelector | Numeric increment/decrement stepper | <div role="group"> with - / + buttons |
🛠️ CLI Usage & Compilation
The package provides the webify command (aliases: react-native-webify, rn-to-react).
Command Options
webify build [options]
Options:
-i, --input <path> React Native source directory (default: ./src)
-o, --output <path> Generated React Web directory (default: ./web)
-w, --watch Watch for file changes and recompile automatically
-h, --help Show help messageConfiguration File (react-native-webify.config.json)
You can place a configuration file in your project root:
{
"input": "./src/components",
"output": "./web/src/generated",
"recursive": true
}Then simply execute:
npx webify build🌐 Next.js & React Web Integration
Integrating with Next.js is straightforward. Add a pre-compilation script in your package.json:
{
"scripts": {
"compile:ui": "webify build -i ../mobile/components -o ./src/components/generated",
"dev": "npm run compile:ui && next dev",
"build": "npm run compile:ui && next build",
"watch:ui": "webify build -i ../mobile/components -o ./src/components/generated --watch"
}
}Because the output is pure React JSX using semantic HTML, it works seamlessly with:
- Server Components (RSC) in Next.js App Router.
- Static Site Generation (SSG) and Server-Side Rendering (SSR).
- Standard bundlers (Vite, Webpack, Turbopack, Rspack).
💻 Programmatic API
You can also use the compiler directly in your custom Node.js build pipelines:
import { transformReactNativeToReact } from "@groooh/react-native-webify/compiler";
const rnCode = `
import React from "react";
import { Card, Heading, Button } from "@groooh/react-native-webify";
export const Welcome = () => (
<Card padding="md">
<Heading variant="h2">Hello Groooh</Heading>
<Button variant="primary">Explore</Button>
</Card>
);
`;
const webCode = transformReactNativeToReact(rnCode, "Welcome.tsx");
console.log(webCode);
// Outputs semantic <article>, <h2>, <button> React code!📊 Feature Comparison
| Feature | @groooh/react-native-webify | react-native-web | Solito |
|---|:---:|:---:|:---:|
| Zero Runtime Overhead | ✅ 0 KB | ❌ ~50–100 KB | ❌ Depends on RNW |
| Semantic HTML Elements | ✅ Native <article>, <h1>, <button>, <dialog> | ❌ Nested <div> & <span> | ❌ Only handles links/routing |
| SEO & Lighthouse Scores | ✅ 100% Native Lighthouse Score | ⚠️ Degraded by non-semantic DOM | ⚠️ Degraded by RNW |
| Next.js Server Components | ✅ 100% Native Support | ⚠️ Needs client boundary wrappers | ⚠️ Mixed |
| Design Token System | ✅ Integrated (Colors, Spacing, Typography) | ❌ Manual | ❌ Bring your own |
| Real-time CLI Watch Mode | ✅ Built-in | ❌ | ❌ |
👨💻 Author
d-naum
- Website: www.groooh.com
- Organization: Groooh
