This is an Expo project created with create-expo-app, showing how TailwindCSS styles can be used in React Native apps.
This app can be built for web, mobile devices (iOS, iPadOS, and Android), and TV devices (Apple TV, Android TV).
You can see the web version at
https://nativewindmultiplatform.expo.app/
Some of the packages used:
- The React Native TV fork, which supports both phone (Android and iOS) and TV (Android TV and Apple TV) targets
- The React Native TV config plugin, to allow Expo prebuild to modify the project's native files for TV builds
- The NativeWind package which lets you use Tailwind CSS in react-native.
- The expo-router native tabs feature in Expo SDK 55
-
cdinto the project -
Build for mobile devices (including iPad tablet support)
yarn
yarn prebuild # Executes a clean Expo prebuild to generate iOS and Android native files
yarn ios # Build and run for iOS
yarn android # Build and run for Android- Build for TV devices
yarn
yarn prebuild:tv # Executes a clean Expo prebuild to generate tvOS and Android TV native files
yarn ios # Build and run for Apple TV
yarn android # Build and run for Android TV- Web (local development)
yarn
yarn web # Runs the web app as a local development server on port 8081- Web (deploy to EAS hosting)
yarn
eas init
yarn build:web # Build the web deployment bundle
yarn deploy:web # Deploy to the EAS hosting dev environmentNOTE: Setting the environment variable
EXPO_TV=1enables the@react-native-tvos/config-tvplugin to modify the project for TV. This can also be done by setting the parameterisTVto true in theapp.json.
The UI is derived from the NativeWind example app plus this Tailwind CSS example. It includes custom CSS transforms.
Theming and dark/light mode support are provided by the global CSS file. It contains dark and light versions of the custom colors used in the app, as well as custom scale factors for scaling buttons when they are focused, hovered, or active. The colors are the same as those used in the default template used when creating new Expo Router apps.
The root layout sets the theme as the style in the root view.
The themed components demonstrate how to use NativeWind class names and the custom colors to provide consistent text, button, and link appearance. These are used both in the home screen and the tab layout for web.
The app provides a native tab layout using expo-router/unstable-native-tabs.
For web and Android TV, the web tab layout uses the custom tab layout feature of Expo Router.
These are shown in the focus/hover/active demo screen.
- The buttons are styled with
focus:bg-blue-300andactive:bg-green-600. On TV, thefocusprefix causes the style to be applied to controls whenonFocus()is invoked, and the style is removed whenonBlur()is invoked. On both TV and mobile, theactiveprefix applies the style whenonPressIn()is invoked, and removes it whenonPressOut()is invoked. - The buttons are also styled with
hover:bg-blue-300, to apply that style when the mouse hovers over the button in the web version of the app. - Finally,
transition duration-500is applied so that the focus, blur, and hover transitions happen smoothly with an animation.
In NativeWind v4, the CSSWrappedComponents.tsx file demonstrated how to wrap components provided by Expo and other packages to enable them to use NativeWind class names, using the cssInterop() API. In NativeWind v5, this is no longer needed — components are imported directly from their original packages. The @expo/rncss-components package in this repository provides CSS wrappers for the components used in the app, using the react-native-css package, and provides a custom Metro config so that imports from react-native and other packages are properly redirected to those wrappers.
- Component imports renamed from PascalCase to kebab-case (e.g.
@/components/ThemedText→@/components/themed-text,@/hooks/useScreenDimensions→@/hooks/use-screen-dimensions) @/layouts/TabLayout→@/components/app-tabs- Removed
CSSWrappedComponentsbarrel import — now imports directly from original packages (SafeAreaViewfromreact-native-safe-area-context,Imagefromexpo-image,LegendListfrom@legendapp/list)
- Square bracket syntax replaced with parenthetical syntax:
bg-[--color-background]→bg-(--color-background) - Custom CSS variable text colors now use
!importantmodifier:text-[--light-theme-fg]→text-(--light-theme-fg)!
ThemedTextnow usescolor="none"andadditionalClassNameprops instead of directclassNameoverridesThemedTextbold/font/animation styles use!importantsuffix (e.g.font-bold!,animate-bounce!)- Removed
useColorSchemetoggle button from the home screen - Image transform classNames changed to named classes (
translateimage,scaleimage) LegendListgainedshowsScrollIndex={false}prop
- The
VideoTestcomponent import was replaced with an inlineVideoDemocomponent featuring direct use ofexpo-video(useVideoPlayer,VideoView), playback controls (play/pause, rewind, seek, fullscreen), a customProgressBarcomponent, and auseIntervalhook for tracking progress
To learn more about developing your project with Expo, look at the following resources:
- Expo documentation: Learn fundamentals, or go into advanced topics with our guides.
- Learn Expo tutorial: Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
- Building Expo apps for TV
Join our community of developers creating universal apps.
- Expo on GitHub: View our open source platform and contribute.
- Discord community: Chat with Expo users and ask questions.