react-strict-dom by EvanBacon
Set up react-strict-dom with Babel, PostCSS, and CSS-wrapped HTML components for universal Expo apps
Content & Writing
542 Stars
24 Forks
Updated Jan 18, 2026, 01:20 AM
Why Use This
This skill provides specialized capabilities for EvanBacon's codebase.
Use Cases
- Developing new features in the EvanBacon repository
- Refactoring existing code to follow EvanBacon standards
- Understanding and working with EvanBacon's codebase structure
Install Guide
2 steps- 1
Skip this step if Ananke is already installed.
- 2
Skill Snapshot
Auto scan of skill assets. Informational only.
Valid SKILL.md
Checks against SKILL.md specification
Source & Community
Repository expo-router-forms-components
Skill Version
main
Community
542 24
Updated At Jan 18, 2026, 01:20 AM
Skill Stats
SKILL.md 278 Lines
Total Files 1
Total Size 0 B
License NOASSERTION
--- name: react-strict-dom description: Set up react-strict-dom with Babel, PostCSS, and CSS-wrapped HTML components for universal Expo apps --- # React Strict DOM Setup for Expo This guide covers setting up react-strict-dom in an Expo project with Tailwind CSS v4 and react-native-css for universal app development. ## Overview react-strict-dom provides a subset of HTML/CSS that works identically across web and native platforms. This setup includes: - **Babel preset** - Transforms react-strict-dom components for each platform - **PostCSS plugin** - Processes styles for web builds - **CSS directive** - Enables react-strict-dom styles in global CSS - **HTML wrapper components** - CSS-wrapped primitives with Tailwind className support ## Installation Install react-strict-dom using bun: ```bash bun install [email protected] ``` > Note: Version 0.0.55 has a broken dependency (`[email protected]` doesn't exist). Use 0.0.54 until this is resolved. ## Babel Configuration Create or update `babel.config.js` in the project root: ```javascript const reactStrictPreset = require("react-strict-dom/babel-preset"); function getPlatform(caller) { return caller && caller.platform; } function getIsDev(caller) { if (caller?.isDev != null) return caller.isDev; // https://babeljs.io/docs/options#envname return ( process.env.BABEL_ENV === "development" || process.env.NODE_ENV === "development" ); } module.exports = function (api) { //api.cache(true); const platform = api.caller(getPlatform); const dev = api.caller(getIsDev); return { presets: [ "babel-preset-expo", [reactStrictPreset, { debug: true, dev, platform }], ], }; }; ``` ### Configuration Options - `debug: true` - Enables debug output during development - `dev` - Automatically detected from environment - `platform` - Passed by Metro/bundler for platform-specific transforms ## PostCSS Configuration Update `postcss.config.mjs` to include the react-strict-dom plugin: ```javascript export default { plugins: { "@tailwindcss/postcss": {}, "react-strict-dom/postcss-plugin": { include: ["src/**/*.{js,jsx,mjs,ts,tsx}"], }, }, }; ``` The `include` option specifies which files to process for react-strict-dom styles. ## Global CSS Directive Add the `@react-strict-dom` directive at the top of your global CSS file (e.g., `src/global.css`): ```css @react-strict-dom; @import "tailwindcss/theme.css" layer(theme); @import "tailwindcss/preflight.css" layer(base); @import "tailwindcss/utilities.css"; /* ... rest of your styles */ ``` This directive must be the first rule in the file for react-strict-dom styles to work correctly. ## HTML Components with Tailwind Support Create CSS-wrapped HTML components that support Tailwind className at `src/html/index.tsx`: ```typescript import { useCssElement } from "react-native-css"; import React from "react"; import { html as rsd } from "react-strict-dom"; import { StyleSheet } from "react-native"; function createCssComponent<T extends keyof typeof rsd>( elementName: T, displayName?: string ) { type Props = React.ComponentProps<(typeof rsd)[T]> & { className?: string; }; let Component: (props: Props) => React.ReactElement; if (process.env.EXPO_OS === "web") { Component = (props: Props) => { // eslint-disable-next-line import/namespace return useCssElement(rsd[elementName], props, { // @ts-expect-error className: "style", }); }; } else { Component = ({ style, ...props }: Props) => { const normal = props as any; if (style) { normal.style = normalizeStyle(style); } return useCssElement( // eslint-disable-next-line import/namespace rsd[elementName], normal, { // @ts-expect-error className: "style", } ); }; } (Component as any).displayName = displayName || `CSS(${elementName})`; return Component; } function normalizeStyle(style: any) { if (!style) return style; const flat = StyleSheet.flatten(style); if (flat.backgroundImage) { flat.experimental_backgroundImage = flat.backgroundImage; delete flat.backgroundImage; } return flat; } export const html = { button: createCssComponent("button"), div: createCssComponent("div"), h1: createCssComponent("h1"), h2: createCssComponent("h2"), h3: createCssComponent("h3"), h4: createCssComponent("h4"), h5: createCssComponent("h5"), h6: createCssComponent("h6"), p: createCssComponent("p"), span: createCssComponent("span"), img: createCssComponent("img"), input: createCssComponent("input"), textarea: createCssComponent("textarea"), a: createCssComponent("a"), ul: createCssComponent("ul"), ol: createCssComponent("ol"), li: createCssComponent("li"), nav: createCssComponent("nav"), header: createCssComponent("header"), footer: createCssComponent("footer"), main: createCssComponent("main"), section: createCssComponent("section"), article: createCssComponent("article"), aside: createCssComponent("aside"), label: createCssComponent("label"), form: createCssComponent("form"), i: createCssComponent("i"), b: createCssComponent("b"), strong: createCssComponent("strong"), em: createCssComponent("em"), code: createCssComponent("code"), pre: createCssComponent("pre"), blockquote: createCssComponent("blockquote"), hr: createCssComponent("hr"), }; ``` ### Usage Import and use the HTML components with Tailwind classes: ```tsx import { html } from "@/html"; export function MyComponent() { return ( <html.div className="flex flex-col gap-4 p-4 bg-sf-bg"> <html.h1 className="text-2xl font-bold text-sf-text"> Hello World </html.h1> <html.p className="text-sf-text-2"> This works on both web and native! </html.p> <html.button className="px-4 py-2 bg-sf-blue text-white rounded-lg"> Click me </html.button> </html.div> ); } ``` ## How It Works ### Web On web, react-strict-dom renders actual HTML elements (`<div>`, `<button>`, etc.) with the PostCSS plugin processing styles. The `useCssElement` wrapper enables Tailwind className support. ### Native (iOS/Android) On native, react-strict-dom transforms HTML elements into equivalent React Native components: - `<div>` → `<View>` - `<span>`, `<p>` → `<Text>` - `<img>` → `<Image>` - etc. The Babel preset handles these transformations at build time. ## Style Normalization The `normalizeStyle` function handles platform-specific style differences: - Flattens style arrays using `StyleSheet.flatten()` - Converts `backgroundImage` to `experimental_backgroundImage` for native compatibility ## Benefits 1. **Semantic HTML**: Write semantic HTML that works everywhere 2. **Better SEO**: Real HTML elements on web 3. **Accessibility**: Native HTML semantics for screen readers 4. **Tailwind Integration**: Full className support with react-native-css 5. **Type Safety**: Full TypeScript support with proper component props 6. **Platform Optimizations**: Platform-specific transforms at build time ## Troubleshooting ### "postcss-react-strict-dom version not found" Use [email protected] instead of the latest version. Version 0.0.55 has a missing dependency. ### Styles not applying on native Ensure the `@react-strict-dom` directive is at the top of your global CSS file. ### TypeScript errors with displayName Use type assertions when setting displayName: ```typescript (Component as any).displayName = displayName; ``` ### backgroundImage not working on native The `normalizeStyle` function handles this automatically by mapping to `experimental_backgroundImage`.
Name Size