https://github.com/finkef/react-native-tailwind.macro
✨ Write universal, responsive Tailwind styles in React Native (+ Web).
https://github.com/finkef/react-native-tailwind.macro
babel-plugin-macros react-native tailwindcss
Last synced: about 1 month ago
JSON representation
✨ Write universal, responsive Tailwind styles in React Native (+ Web).
- Host: GitHub
- URL: https://github.com/finkef/react-native-tailwind.macro
- Owner: finkef
- Created: 2021-09-16T14:01:25.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2022-10-20T17:34:35.000Z (over 2 years ago)
- Last Synced: 2025-02-16T12:16:12.344Z (4 months ago)
- Topics: babel-plugin-macros, react-native, tailwindcss
- Language: TypeScript
- Homepage:
- Size: 2.4 MB
- Stars: 40
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
Awesome Lists containing this project
README
# react-native-tailwind.macro ✨


> Babel macro for easily writing responsive Tailwind styles in React Native (+ Web).
---
Easily style components using the `tw` prop:
```ts
import "react-native-tailwind.macro"
import { View, Text } from "react-native"const Badge = () => (
Badge
)
```Apply conditions and create memoized and complex styles using `useTailwindStyles`:
```ts
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 (
{isActive ? "Active" : "Inactive"}
)
}
```---
- [Features](#features)
- [Installation](#installation)
- [Usage](#usage)
- [Using the tw prop](#using-the-tw-prop)
- [useTailwindStyles](#usetailwindstyles)
- [TailwindProvider](#tailwindprovider)
- [Macro Options](#macro-options)
- [Next.js SSR Setup](#nextjs-ssr-setup)
- [How it works](#how-it-works)
- [Caveats](#caveats)
- [Credits](#credits)
- [Contributing](#contributing)
- [License](#license)## 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](https://github.com/jaredh159/tailwind-react-native-classnames#adding-custom-classes) made possible by the amazing [tailwind-react-native-classnames](https://github.com/jaredh159/tailwind-react-native-classnames)
## Installation
This library relies on [babel-plugin-macros](https://github.com/kentcdodds/babel-plugin-macros).
```sh
yarn add --dev babel-plugin-macrosyarn add react-native-tailwind.macro
```Add babel-plugin-macros to your `.babelrc` or `babel.config.js` and you're all set up!
```json
{
"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:**
```ts
import "react-native-tailwind.macro"
import { View } from "react-native"
import { MyComponent } from "./my-component"const Example = () => (
<>
{/* Only works if MyComponent accepts a "style" prop */}
>
)
```⚠️ **NOTE:** In order for `` 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:
```ts
const MyComponent = ({ disabled, ...props }) => (
{disabled ? "Disabled" : "Enabled"}
)
```### `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.
```ts
import { useTailwindStyles } from "react-native-tailwind.macro"
import { ScrollView } from "react-native"const Example = () => {
const styles = useTailwindStyles((tw) => ({
contentContainer: tw`py-8`,
}))return (
{/* Content */}
)
}
```This also comes in handy, when you want to apply styles conditionally, pass in [Reanimated](https://github.com/software-mansion/react-native-reanimated) animated styles or do any other fancy stuff with your styles.
```ts
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 (
<>
(offset.value = Math.random())}
>
Move
>
)
}
```#### 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](https://gist.github.com/mrousavy/0de7486814c655de8a110df5cef74ddc#react-native) for explanation.
```ts
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.
```ts
import {
TailwindProvider,
getInitialColorScheme,
} from "react-native-tailwind.macro"const App = () => {
const [darkMode, setDarkMode] = useState(getInitialColorScheme() === "dark")return {/* ... */}
}
```### `getInitialColorScheme`
Returns either the cookie-persisted preference on web or falls back to the system preference.
```ts
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:
```js
// babel-plugin-macros.config.js
module.exports = {
reactNativeTailwind: {
// your options
},
}
```Alternatively:
```js
// package.json
{
//...
"babelMacros": {
"reactNativeTailwind": {
// your options
}
}
}
```Available 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](https://nextjs.org/docs/advanced-features/custom-document) as follows:
```diff
// 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
```ts
import "react-native-tailwind.macro"const Example = () => (
)
```... to something along the lines of this:
```ts
// 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 (
)
}
```For more examples and use cases, check the [macro test snapshots](packages/react-native-tailwind.macro/src/__snapshots__/macro.test.ts.snap).
## 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
- `` and `` tw`...` `` only accept static styles without string interpolation
## Credits
- [tailwind-react-native-classnames](https://github.com/jaredh159/tailwind-react-native-classnames): Used for compiling Tailwind styles
- [react-native-media-query](https://github.com/kasinskas/react-native-media-query): Provides the base implementation used to enable CSS media query support
- [twin.macro](https://github.com/ben-rogerson/twin.macro): Inspiration for writing a babel macro
## Contributing
See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
## License
MIT