Skip to main content

Web Platform Support

NativeWind provides first-class support for React Native Web, allowing you to use the same Tailwind CSS styling across native and web platforms. NativeWind automatically uses CSS stylesheets on web for optimal performance.
This guide covers adding web support to React Native apps. For Expo projects, web support is built-in. See the Expo Setup Guide.

How It Works

NativeWind uses different rendering strategies for optimal performance on each platform:
1

Native Platforms (iOS/Android)

Styles are compiled to React Native’s StyleSheet.create API, providing native performance.
2

Web Platform

Styles are rendered as CSS stylesheets, leveraging browser optimizations for the best web performance.
3

Universal API

You write the same className prop code that works across all platforms seamlessly.

Prerequisites

Before adding web support, ensure you have:
  • Existing React Native project with NativeWind configured
  • Node.js 20+ installed
  • webpack or Metro bundler configured

Installation

For Expo Projects

Expo includes web support out of the box:
npx expo install react-dom react-native-web
Then run:
npm run web
# or
expo start --web
Expo projects automatically configure webpack for web. See Expo Setup Guide for full configuration.

For React Native CLI Projects

1

Install Dependencies

Install React Native Web and related packages:
npm install react-native-web react-dom
npm install --save-dev webpack webpack-cli webpack-dev-server
npm install --save-dev babel-loader html-webpack-plugin
npm install --save-dev @babel/preset-react @babel/preset-typescript
2

Configure webpack

Create webpack.config.js in your project root:
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './index.web.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
  mode: 'development',
  module: {
    rules: [
      {
        test: /\.(js|jsx|ts|tsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader', 'postcss-loader'],
      },
    ],
  },
  resolve: {
    alias: {
      'react-native$': 'react-native-web',
    },
    extensions: ['.web.js', '.web.ts', '.web.tsx', '.js', '.ts', '.tsx', '.json'],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html',
    }),
  ],
  devServer: {
    static: {
      directory: path.join(__dirname, 'public'),
    },
    port: 3000,
    hot: true,
  },
};
3

Create Web Entry Point

Create index.web.js in your project root:
index.web.js
import { AppRegistry } from 'react-native';
import App from './App';
import './global.css';

AppRegistry.registerComponent('YourApp', () => App);
AppRegistry.runApplication('YourApp', {
  rootTag: document.getElementById('root'),
});
4

Create HTML Template

Create public/index.html:
public/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <title>Your App</title>
    <style>
      body {
        margin: 0;
        padding: 0;
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
          'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
          sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
      }
      #root {
        display: flex;
        height: 100vh;
        width: 100vw;
      }
    </style>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>
5

Update package.json Scripts

Add web scripts to package.json:
package.json
{
  "scripts": {
    "web": "webpack serve --mode development",
    "build:web": "webpack --mode production"
  }
}

Configuration

PostCSS Configuration

Ensure postcss.config.mjs is properly configured:
postcss.config.mjs
export default {
  plugins: {
    '@tailwindcss/postcss': {},
  },
};

CSS Loaders for webpack

Install CSS loaders:
npm install --save-dev style-loader css-loader postcss-loader
These loaders enable webpack to process CSS files and apply PostCSS transformations.

Web-Specific Styling

NativeWind provides web-specific utilities and behaviors:

Platform Modifiers

Use the web: modifier for web-only styles:
import { View, Text } from 'react-native';

function Card() {
  return (
    <View className="p-4 web:hover:shadow-xl web:cursor-pointer">
      <Text className="text-lg web:select-text">
        This card has web-specific interactions
      </Text>
    </View>
  );
}

Hover States

Hover states work automatically on web:
import { Pressable, Text } from 'react-native';

function Button() {
  return (
    <Pressable className="bg-blue-500 hover:bg-blue-600 px-4 py-2 rounded">
      <Text className="text-white">Hover me</Text>
    </Pressable>
  );
}
Hover states work on web by default. On native platforms, use active: for press states.

Focus States

import { TextInput } from 'react-native';

function Input() {
  return (
    <TextInput
      className="border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 px-3 py-2 rounded"
      placeholder="Focus me"
    />
  );
}

Cursor Utilities

Web-specific cursor styles:
<View className="web:cursor-pointer" />
<View className="web:cursor-not-allowed" />
<View className="web:cursor-wait" />

Responsive Design

NativeWind’s responsive utilities work seamlessly on web:
function ResponsiveLayout() {
  return (
    <View className="flex-col md:flex-row gap-4 p-4">
      <View className="flex-1 bg-blue-100 p-4 rounded-lg">
        <Text className="text-sm md:text-base lg:text-lg">
          Responsive text size
        </Text>
      </View>
      <View className="flex-1 bg-green-100 p-4 rounded-lg">
        <Text>Second column</Text>
      </View>
    </View>
  );
}
Breakpoints:
  • sm: - 640px
  • md: - 768px
  • lg: - 1024px
  • xl: - 1280px
  • 2xl: - 1536px

Web-Specific Components

Some components behave differently on web:

ScrollView vs. div

import { ScrollView, View } from 'react-native';

function WebOptimizedScroll() {
  return (
    <ScrollView className="flex-1 web:overflow-auto">
      <View className="p-4">
        {/* Content */}
      </View>
    </ScrollView>
  );
}
For web navigation, consider using web-specific routing:
// Using a web router like React Router
import { Pressable, Text } from 'react-native';

function NavLink({ href, children }) {
  return (
    <Pressable
      className="hover:bg-gray-100 px-4 py-2 rounded"
      onPress={() => {
        // Handle navigation
        if (typeof window !== 'undefined') {
          window.location.href = href;
        }
      }}
    >
      <Text className="text-blue-600 hover:text-blue-800">{children}</Text>
    </Pressable>
  );
}

Performance Optimization

Code Splitting

For better web performance, use code splitting:
webpack.config.js
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
    },
  },
};

CSS Extraction

For production builds, extract CSS:
npm install --save-dev mini-css-extract-plugin
webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          process.env.NODE_ENV === 'production'
            ? MiniCssExtractPlugin.loader
            : 'style-loader',
          'css-loader',
          'postcss-loader',
        ],
      },
    ],
  },
};

SEO Considerations

Meta Tags

Add proper meta tags in index.html:
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta name="description" content="Your app description" />
  <meta property="og:title" content="Your App" />
  <meta property="og:description" content="Your app description" />
  <title>Your App</title>
</head>

Server-Side Rendering (SSR)

For SSR support, consider using frameworks like:
  • Next.js with react-native-web
  • Remix with custom React Native Web integration

Troubleshooting

  1. Verify global.css is imported in your web entry point
  2. Check webpack CSS loaders are configured correctly
  3. Ensure PostCSS is processing the CSS file
  4. Clear webpack cache: rm -rf dist node_modules/.cache
  1. Check webpack resolve.alias includes react-native-web
  2. Verify resolve.extensions includes .web.js, .web.ts, .web.tsx
  3. Ensure React Native Web is installed correctly
  1. Use :hover and :focus pseudo-classes (they work automatically on web)
  2. For native platforms, use active: for press states
  3. Verify Pressable components are used instead of View for interactive elements
  1. Enable production mode: webpack --mode production
  2. Implement code splitting
  3. Extract CSS in production builds
  4. Use webpack bundle analyzer to identify large modules
  1. Ensure dark: classes are used
  2. Check browser/OS dark mode settings
  3. Implement manual dark mode toggle if needed
  4. Verify CSS media queries are working: @media (prefers-color-scheme: dark)

Advanced Web Features

Custom Fonts on Web

global.css
@font-face {
  font-family: 'CustomFont';
  src: url('./fonts/CustomFont.woff2') format('woff2');
  font-weight: normal;
  font-style: normal;
}
<Text className="font-[CustomFont]">Custom font text</Text>

Web Animations

Use CSS animations on web:
global.css
@keyframes slideIn {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(0);
  }
}
<View className="web:animate-[slideIn_0.3s_ease-out]">
  <Text>Animated content</Text>
</View>

Progressive Web App (PWA)

Convert your app to a PWA by adding a manifest:
public/index.html
<link rel="manifest" href="/manifest.json" />
public/manifest.json
{
  "name": "Your App",
  "short_name": "App",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#000000",
  "icons": [
    {
      "src": "/icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

Deployment

Static Hosting

Build for production and deploy to static hosting:
npm run build:web
Deploy the dist folder to:
  • Vercel: vercel deploy
  • Netlify: netlify deploy --prod --dir=dist
  • GitHub Pages: Use GitHub Actions for automated deployment
  • AWS S3: Upload dist folder to S3 bucket

Environment Variables

For different environments:
webpack.config.js
const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      'process.env.API_URL': JSON.stringify(process.env.API_URL || 'http://localhost:3000'),
    }),
  ],
};

Next Steps

Responsive Design

Learn responsive design patterns for web and native

Dark Mode

Implement dark mode across all platforms

Custom Styles

Add custom utilities and styles

Troubleshooting

Common issues and solutions