npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-native-tailwind.macro

v1.3.3

Published

Babel macro for writing Tailwind styles in React Native (+ Web)

Downloads

1,229

Readme

react-native-tailwind.macro ✨

npm version github workflow status

demo

Babel macro for easily writing responsive Tailwind styles in React Native (+ Web).


Easily style components using the tw prop:

import "react-native-tailwind.macro"
import { View, Text } from "react-native"

const Badge = () => (
  <View tw="px-3 py-0.5 rounded-xl ios:rounded-full bg-blue-100 dark:bg-blue-800">
    <Text tw="text-sm lg:text-lg font-medium text-blue-800">Badge</Text>
  </View>
)

Apply conditions and create memoized and complex styles using useTailwindStyles:

import { useTailwindStyles } from "react-native-tailwind.macro"
import { View, Text } from "react-native"

const Status = ({ isActive, labelColor }) => {
  const { box, text } = useTailwindStyles(
    (tw) => ({
      box: [tw`rounded-3xl px-8 py-4 bg-gray-100`, isActive && tw`bg-blue-800`],
      text: [tw`font-medium`, isActive && tw`font-bold`, { color: labelColor }],
    }),
    [isActive, labelColor]
  )

  return (
    <View style={box}>
      <Text style={text}>{isActive ? "Active" : "Inactive"}</Text>
    </View>
  )
}

Features

  • ⚡️   Responsive styles on native and web (with CSS Media Queries and SSR support - no more layout flashes!)

    bg-blue-500 md:bg-purple-500 xl:bg-indigo-500

  • 🌓   Dark Mode

    text-black dark:text-white

  • 📱   Platform selectors

    ios:bg-blue-800 android:bg-purple-800

  • 🖥   Web only selectors

    focus:bg-blue-400 active:bg-indigo-400

  • 🕺   Stackable selectors

    ios:md:font-bold android:(text-blue-800 sm:dark:text-blue-100)

  • 🧠   All styles are statically generated at build time and memoized

  • 🛠   useTailwindStyles hook for optimized conditional and complex styles

  • 📝   Respects your tailwind.config.js and allows for custom classes and plugins made possible by the amazing tailwind-react-native-classnames

Installation

This library relies on babel-plugin-macros.

yarn add --dev babel-plugin-macros

yarn add react-native-tailwind.macro

Add babel-plugin-macros to your .babelrc or babel.config.js and you're all set up!

{
  "plugins": ["macros"]
}

Usage

Using the tw prop

The best and easiest usage is to simply use the tw prop that is artificially added to all JSX elements. Under the hood, the macro removes the tw prop completely and instead applies or extends a style prop and also adds a web-only data-tw id used to apply CSS-based media queries.

All you have to do is have any import of react-native-tailwind.macro in your file, either import "react-native-tailwind.macro" or import { /* whatever import you need */ } from "react-native-tailwind.macro".

Example:

import "react-native-tailwind.macro"
import { View } from "react-native"
import { MyComponent } from "./my-component"

const Example = () => (
  <>
    <View tw="w-[100px] h-[100px] bg-blue-500 md:bg-purple-500" />

    {/* Only works if MyComponent accepts a "style" prop */}
    <MyComponent tw="bg-pink-500 ios:dark:bg-indigo-800" />
  </>
)

⚠️ NOTE: In order for <MyComponent /> to render responsive styles on the web, you need to also pass down a dataSet prop to the element receiving the style. The easiest way to achieve this would be to use the rest-spread syntax for MyComponent's props and pass all non-used props to the style-carrying element:

const MyComponent = ({ disabled, ...props }) => (
  <View {...props}>
    <Text>{disabled ? "Disabled" : "Enabled"}</Text>
  </View>
)

useTailwindStyles

In cases where you don't have access to a style prop or need to apply styles using other props, like contentContainerStyle on a ScrollView, you can use useTailwindStyles to produce the desired style objects.

import { useTailwindStyles } from "react-native-tailwind.macro"
import { ScrollView } from "react-native"

const Example = () => {
  const styles = useTailwindStyles((tw) => ({
    contentContainer: tw`py-8`,
  }))

  return (
    <ScrollView contentContainerStyle={styles.contentContainer} {/* ... */}>
      {/* Content */}
    </ScrollView>
  )
}

This also comes in handy, when you want to apply styles conditionally, pass in Reanimated animated styles or do any other fancy stuff with your styles.

import { useTailwindStyles } from "react-native-tailwind.macro"
import { TouchableOpacity, Text } from "react-native"
import Animated, {
  useSharedValue,
  useAnimatedStyle,
} from "react-native-reanimated"

const Example = ({ rounded, backgroundColor }) => {
  // Reanimated 🔥
  const offset = useSharedValue(0)
  const animatedStyles = useAnimatedStyle(() => {
    return {
      transform: [{ translateX: offset.value * 255 }],
    }
  })

  const styles = useTailwindStyles(
    (tw) => ({
      box: [
        tw`w-[100px] h-[100px] lg:(w-[300px] h-[300px])`,
        rounded && tw`rounded-3xl`, // conditional Tailwind style
        animatedStyles, // apply Reanimated styles
        { backgroundColor }, // apply passed in backgroundColor
      ],
      text: tw`text-lg font-bold`,
    }),
    // only recompute when any of these deps change, skip all unnecessary re-renders
    [rounded, animatedStyles, backgroundColor]
  )

  return (
    <>
      <Animated.View
        style={styles.box}
        {/* Required for responsive styles on the web, hence it's preferred to apply all responsive styles through the `tw` prop */}
        dataSet={{ tw: styles.box.id }}
      />
      <TouchableOpacity
        {/* Mix and match tw prop and styles in your code */}
        tw="mt-8"
        onPress={() => (offset.value = Math.random())}
      >
        <Text style={styles.text}>Move</Text>
      </TouchableOpacity>
    </>
  )
}

Pro Tip 👻

Even if you don't rely on any Tailwind styles, useTailwindStyles can be used to generate memoized styles as performance boost, see this gist for explanation.

const styles = useTailwindStyles(
  (tw) => ({
    box: [{ backgroundColor: "red" }, isActive && { backgroundColor: "pink" }],
  }),
  [isActive]
)

TailwindProvider

By default, the device's color scheme preference is used to enable dark mode. If you want to dynamically change whether dark mode is enabled, you can wrap your App with TailwindProvider and pass in your dark mode preference.

On the web, the set value will automatically be persisted in a cookie to enable SSR and SSG without flashes on load.

import {
  TailwindProvider,
  getInitialColorScheme,
} from "react-native-tailwind.macro"

const App = () => {
  const [darkMode, setDarkMode] = useState(getInitialColorScheme() === "dark")

  return <TailwindProvider dark={darkMode}>{/* ... */}</TailwindProvider>
}

getInitialColorScheme

Returns either the cookie-persisted preference on web or falls back to the system preference.

import { getInitialColorScheme } from "react-native-tailwind.macro"

getInitialColorScheme() // returns "light" or "dark"

Macro Options

You can apply options to the macro by adding a babel-plugin-macros.config.js or specifying them in your package.json like below:

// babel-plugin-macros.config.js
module.exports = {
  reactNativeTailwind: {
    // your options
  },
}

Alternatively:

// package.json
{
  //...
  "babelMacros": {
    "reactNativeTailwind": {
      // your options
    }
  }
}

| Name | Default | Description | | ------ | ------------------------ | --------------------------------- | | config | "./tailwind.config.js" | The path to your Tailwind config. |

Next.js SSR Setup

In order to enable SSR support via media queries on Next.js, update your custom document as follows:

// pages/_document.js

+ import { flush } from "react-native-tailwind.macro"

/* ... */

export class Document extends NextDocument {
  static async getInitialProps({ renderPage }) {
    AppRegistry.registerComponent("Main", () => Main)
    const { getStyleElement } = AppRegistry.getApplication("Main")
    const page = renderPage()
    const styles = [
      getStyleElement(),
+     flush(),
    ]
    return { ...page, styles: React.Children.toArray(styles) }
  }

  render() {
    /* ... */
  }
}

How it works

Behind the scenes, react-native-tailwind.macro turns your simple code from this

import "react-native-tailwind.macro"

const Example = () => (
  <View tw="w-[100px] h-[100px] bg-purple-500 dark:ios:lg:bg-pink-500 hover:bg-indigo-500" />
)

... to something along the lines of this:

// Import the necessary utilities
import * as ReactNativeTailwindMacro from "react-native-tailwind.macro/exports"

// Creates a hook based on the static output from Tailwind style compilation
const useStyles = ReactNativeTailwindMacro.createUseTailwindStyles({
  // Compiled Tailwind styles with unique id and information on when to apply
  a7gsbs: [
    {
      dark: false,
      selectors: [],
      style: {
        width: 100,
        height: 100,
        backgroundColor: "#8b5cf6",
      },
    },
    {
      dark: true,
      breakpoint: {
        label: "lg",
        minWidth: "1024px",
      },
      selectors: [],
      platform: "ios",
      style: {
        backgroundColor: "#ec4899",
      },
    },
    {
      dark: false,
      // Styles on web will only be applied on web
      selectors: ["hover"],
      style: {
        backgroundColor: "#6366f1",
      },
    },
  ],
})

const Example = () => {
  // Call to the produced hook, takes into account the current context and returns
  // memoized styles that only change when the context changes
  const tailwindStyles = useStyles()

  return (
    <View
      // Apply the memoized style
      style={tailwindStyles["a7gsbs"]}
      // Apply data-tw id for CSS-based media queries
      dataSet={{ tw: tailwindStyles["a7gsbs"].id }}
    />
  )
}

For more examples and use cases, check the macro test snapshots.

Caveats

  • Only works in function components due to dependency on context

  • useTailwindStyles doesn't properly support responsive styles on the web, prefer to use the tw prop for responsive styles if possible

  • <View tw="..."/> and tw`...` only accept static styles without string interpolation

Credits

Contributing

See the contributing guide to learn how to contribute to the repository and the development workflow.

License

MIT