•
6 September 2025
•
7 mins
This post documents how I imported a React Native component library into a Next.js pages‑router app and rendered it on the web using React Native Web, @expo/next-adapter, and Babel. This configuration is a viable option for preventing duplicate code across a mobile + NextJS app codebase.
@expo/next-adapter
react-native-css-interop
.babelrc
(Next disables SWC when a custom Babel config is present)@isaacaddis/private-rn-library
), RN primitives (@rn-primitives/*
)globals.css
use @import "tailwindcss";
(not v3’s @tailwind base; ...
).className
without CSS interop. Map the primitives you actually render.Pressable
under the hood — interop it specifically.transpilePackages
or you’ll hit syntax/runtime errors.important: "html"
so Tailwind wins over other stylesheets.@isaacaddis/private-rn-library
).transpilePackages
so Next transpiles it for the browser.content
so utility classes used inside it are generated.View
, Text
, TouchableOpacity
, and especially Pressable
for triggers/buttons).After these steps, components can be imported and used as follows:
import { Card, Dialog, DialogTrigger } from "@isaacaddis/private-rn-library";
import { Text } from "react-native";
export default function Page() {
return (
<div className="p-6">
<Card className="rounded-xl border p-4">
<Text>Card content</Text>
</Card>
<Dialog>
<DialogTrigger className="bg-blue-600 p-3 rounded">
<Text className="text-white">Open</Text>
</DialogTrigger>
</Dialog>
</div>
);
}
import { withExpo } from "@expo/next-adapter";
/** @type {import('next').NextConfig} */
const nextConfig = withExpo({
reactStrictMode: true,
transpilePackages: [
"react-native",
"react-native-web",
"nativewind",
"react-native-css-interop",
"@rn-primitives",
"@isaacaddis/private-rn-library",
"react-native-reanimated",
],
webpack: (config) => {
config.resolve.alias = {
...(config.resolve.alias || {}),
"react-native$": "react-native-web",
"phosphor-react-native": "phosphor-react",
};
return config;
},
});
export default nextConfig;
jsxImportSource
line){
"compilerOptions": {
"jsxImportSource": "nativewind",
"jsx": "preserve",
"moduleResolution": "bundler",
"strict": true
},
"include": ["next-env.d.ts", "nativewind-env.d.ts", "**/*.ts", "**/*.tsx"]
}
{
"presets": ["next/babel", "@babel/preset-env", "@babel/preset-flow"],
"plugins": [
[
"@babel/plugin-transform-react-jsx",
{
"runtime": "automatic",
"importSource": "nativewind"
}
]
]
}
/// <reference types="nativewind/types" />
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./pages/**/*.{ts,tsx,js,jsx}",
"./components/**/*.{ts,tsx,js,jsx}",
"./node_modules/@isaacaddis/private-rn-library/**/*.{ts,tsx,js,jsx}",
],
presets: [require("nativewind/preset")],
important: "html",
theme: {
extend: {
// tokens (colors, sizes, etc.)
},
},
plugins: [],
};
const config = {
plugins: ["@tailwindcss/postcss"],
};
export default config;
@import "tailwindcss";
import "@/styles/globals.css";
import type { AppProps } from "next/app";
import { cssInterop } from "react-native-css-interop";
import { View, Text, TouchableOpacity, Pressable } from "react-native";
// Map className -> style for primitives actually rendered in your app/libs
cssInterop(View, { className: "style" });
cssInterop(Text, { className: "style" });
cssInterop(TouchableOpacity, { className: "style" });
cssInterop(Pressable, { className: "style" }); // critical for many Trigger components
export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}
@import "tailwindcss";
in globals.css
.border-red-500
doesn’t
Pressable
without CSS interop.cssInterop(Pressable, { className: "style" })
.transpilePackages
and ensure the library ships browser‑compatible JS.important: "html"
to Tailwind config to increase specificity.Using @expo/next-adapter with React Native Web, Tailwind v4, NativeWind, react-native-css-interop, and Babel allows for importing a React Native library inside a Next.js web app without duplicating UI code. The required steps are: transpile React Native and the library, use Tailwind v4’s @import
CSS, include the library paths in Tailwind content
, and map React Native primitives (including Pressable
) with CSS interop so className
resolves to styles.
Like it? Share it!