Skip Navigation code drift

A Guide to Tailwind CSS in React Native

written  &  updated
in Code, but also CSS, JavaScript, & React Native
🆕
2021-12-20: I'm currently using twrnc which is an evolution of the tailwind-rn library. A pattern like gust is great for custom components, but right now, the React Native ecosystem almost always needs to pass style objects around.

React Native is very behind the web when it comes to UI libraries. There’s two likely reasons for this: CSS on the web is easy and the object known as StyleSheet in React Native is not.

At a fundamental level, the syntax of StyleSheet causes a lot of problems. It looks like CSS (the kind you’d use in React’s style prop), it acts like CSS (most of the time) just like you’d expect in a browser, but then you encounter one of dozens of scenarios where React Native takes the CSS specification and yeets it out the nearest window.

  • Shadows are a custom object syntax instead of a conversion of box-shadow
  • Random elements such as ScrollView introduce custom non-standard props such as elevation, but also only for specific platforms
  • You get flexbox. Only flexbox. Everything flexes. Everything is boxes. Except for the flex property which has decided it is not actually flex as you’d expect but just a number that translates to a percentage. But not a useful number-to-percentage like 55 meaning 55%. Instead, you get an alternate universe number that is expressed as a ratio based on the sum of all flex properties on sibling nodes. It’s a universe where flex:1 means both 100% and 50% based on elements outside of its rendering tree. /rant

Yes, a lot of this will be fixed over time as React Native continues to improve on its style interpreter, but we are still converting from CSS to platform-specific layout calls. I don’t think we’ll ever reach parity, but I’d also like to avoid the programming equivalent of “hammering my thumb” every time I lay things out in a mobile app.

The truth is, what I want is something as easy as Tailwind CSS in React Native. If you haven’t tried Tailwind yet, you should. It’s trivial to make something look good, and it’s adaptable enough withstand design changes. In React Native, there's a few really solid options:

  • tailwind-rn(github, npm) is the OG of Tailwind in your React Native projects. It works by converting the Tailwind styles into JSON, which is memoized in your app on access. Because it tries to be only a translation layer between systems, complex behaviors such as breakpoints and React Native's Appearance are not supported out of the box.
  • tailwind-rn-gust (archived github, npm) extends the tailwind-rn library by adding utilities for custom component creation. By introducing a React Component generator, it's possible to add additional props to turn "on" flags inside of a call to tailwind-rn's style generator. _However, I wouldn't recommend this, as the components add an additional layer of complexity once you want to use Animated.View or other 3rd party components that want to leverage the style prop for stylistic behavior.
  • twrnc (github, npm) Is what I'm currently using and it leverages Tailwind's JIT Compiler to remove the built steps inherent in other Tailwind-bridging systems. Out of the box, it uses React Native's hooks for figuring out screen sizes and appearance, making it easier to use normal Tailwind prefixes such as lg:text-xl.

Originally, this post focused on tailwind-rn-gust, but I think it's becoming more clear that until there's an official Tailwind for React Native, twrnc is going to be the truest-to-Tailwind experience you'll find on the platform.

Instead, here's how you'd accomplish something like a rich "press" interaction using twrnc. (You can also put both styles into a single tw.style call, but I find it easier to read leveraging the array syntax supported for multiple style objects.)

<MyComponent style={({pressed}) => [
  tw`bg-red-500`,
  tw.style({
    "bg-blue-500": pressed
  })
]} />

The best thing about the tw helper is that it's built for generating style objects, meaning it'll work with Moti, Reanimated 2, and many other component libraries that focus on the style prop.

Gust Archived

And so, with great alternatives out there, gust is archived. The entire library is only 159 lines and is easy to port into your own existing React Native project if you need the features. However, until we see something like a native className in React Native, the style tag is going to be the default operating mode for rich styles, animation, and state based effects.