Repository navigation
v3 - nativewind@next #308
Description
NativeWind 3.0 is a major update for NativeWind that fixes a large variety of issues. For most users the upgrade will be simple and should only require a small configuration change.
Breaking Changes
Web exclusively uses CSS styles
NativeWind on web now only works with CSS styling. The minimum version of React Native Web is now v0.18.
There is known issue where React Native Web crashes when using NativeWind on
Animatedcomponents.
You will need to update to the React Native Web 0.19 preview
Project setup
Native apps now require a Metro plugin
// Example metro.config.js for an Expo project
// Expo is not required, you just need to wrap your config using withNativewind
const { getDefaultConfig } = require("expo/metro-config");
const withNativewind = require("nativewind/metro");
module.exports = withNativewind(getDefaultConfig(__dirname));Babel now uses a preset instead of a plugin
// babel.config.js
module.exports = function (api) {
api.cache(true);
return {
- plugins: ["nativewind/babel"]
presets: [
"babel-preset-expo",
+ "nativewind/babel"
],
};
};Tailwind now requires a preset instead of a plugin
// tailwind.config.js
+ const nativewind = require("nativewind/tailwind")
module.exports = {
content: ["./App.{js,jsx,ts,tsx}", "./<custom directory>/**/*.{js,jsx,ts,tsx}"],
+ presets: [nativewind],
- plugins: [nativewind],
theme: {
extend: {},
},
}Base scaling has changed
NativeWind now supports the rem unit. This is a breaking change as it will affect all rem based styles.
Previously, NativeWind 1-1 matched the scaling of TailwindCSS documentation and overrode the rem values with their px equivalent. (example scaling)
This was undesirable, as components styled with NativeWind were different to the base component. For example, NativeWind's text was slightly larger than the default.
<Text> // fontSize: 14
<Text className="text-base" /> // fontSize: 16
Now that NativeWind supports rem, we can use an rem value of 14 to match the default <Text />.
The result of this is your app may "shrink", as everything scales down from the previous static 16px to the new 14.
You can restore the old behaviour by setting NativeWindStyleSheet.setVariables({ '--rem': 16 })
Dark mode
- React Native projects will need to set
darkMode: 'class'to enable manually controlling the color scheme - React Native Web projects will need to follow the Tailwind documentation if they wish for dark mode to honor the system preference while in
darkMode: class
This aligns NativeWind's functionality with TailwindCSS handling of Dark Mode
styled() props options
The classProps option for styles has been removed. This has been merged into props.
The option baseClassName has been renamed to className
The props option still allows you to style non-style props but is now more flexible. Like before it is an object that accepts the name of component props, and it's value configures how that prop is mapped.
If the value is a Boolean, NativeWind will so transform that prop (but not rename it)
If the value is a String, NativeWind will transform and rename that prop. This is useful if you have a prop innerStyle but you'd like to expose it as innerClassName
Lastly, it accepts an object with the option
{
name?: string,
value?: keyof Style,
class?: Boolean
}
name renames the prop, same as simply providing a string.
value extracts the style value from the ste object. This is the same behaviour that NativeWind used to do if you provide a string.
class a Boolean that indicates that this prop should only be transformed on native. On web, it will be pre-pended to the className string. This replaces the old classProps option.
Web className merging
Styles on native and web behaves slightly differently. Native styles are merged together with later classNames overriding earlier ones. However on the web classNames are prioritized based upon their CSS specificity and not the supplied order.
Normally this isn't an issue and can be easily overcome with the important modifier, however for larger apps you may want a more permanent solution. NativeWind exposes NativeWindStyleSheet.setWebClassNameMergeStrategy which you can use with libraries like tailwind-merge to make web behave more like the predictable native styles.
Theme functions
Theme functions like hairlineWidth and platformSelect are now exported from nativewind/theme. You will need to update your imports.
group and parent
group-isolate has been replaced with nested groups. This feature requires TailwindCSS 3.2
The parent variant has been removed and should be replaced with nested groups. The parent class still exists, but is only used for new variants like odd:/even:
Removed features
- NativeWind no longer exports a PostCSS plugin. This was mostly used for troubleshooting, for which there are now better methods.
- The polyfill for
aspectwith ratio values has been removed. Has been added to React Native . If you require this feature and cannot upgrade you can define your aspect ratios in your theme. NativeWindStyleSheet.setOutput()has been removed
New Features
Variant support
styled() has been updated to include variant support allowing for more complex and flexible components.
import { Pressable } from "react-native"
import { styled, sharedProps } from "nativewind"
// Add variant to styled() components
const Button = styled(Pressable, "border rounded", {
variants: {
intent: {
primary: "bg-blue-500 border-transparent hover:bg-blue-600"
secondary: "bg-white border-gray-400 hover:bg-gray-100",
},
size: {
small: "py-1 px-2",
medium: "py-2 px-4",
},
},
defaultProps: {
intent: "primary",
size: "medium",
},
});
const ButtonText = styled(Text, "font-semibold", {
variants: {
intent: {
primary: "text-white"
secondary: "text-gray-800",
},
size: {
small: "text-sm",
medium: "text-base",
},
},
defaultProps: {
intent: "primary",
size: "medium",
},
});
export function MyButton({ intent, size, ...props }) {
return (
<Button intent={intent} size={size} {...props}>
<ButtonText intent={intent} size={size}>Hello, World</ButtonText>
</Button>
)
}The function signature for styled is
styled(Component: ComponentType, defaultClassNames?: string, options?: StyledOptions)
styled(Component: ComponentType, options?: StyledOptions)
variants
The variants option is an schema for what classNames to apply when a prop matches the value.
variants: {
// When <Text size="<size>" /> is set - apply the appropriate style
size: {
small: "text-sm",
base: "text-base",
},
// When <Text bold /> is set - apply the bold className, otherwise apply the regular className
bold: {
true: "font-bold",
false: "font-regular",
},
},
}compoundVariants
{
// When BOTH intent="primary" and size="medium", apply the uppercase className
compoundVariants: [{
intent: "primary",
size: "medium",
className: "uppercase"
}],
}defaultProps
{
size: {
small: "text-sm",
base: "text-base",
},
// When size is not set - apply the base variant
defaultProps: {
size: "base",
},
}Additional usage
Variants are not only limited to styled(), you can write the variant logic inline
import { Pressable } from "react-native"
import { variants, VariantProps } from "nativewind"
const button = variants({
size: {
small: "text-sm",
base: "text-base",
},
})
type MyButtonProps = VariantProps<typeof button>
export default function MyButton(props: MyButtonProps) {
const className = button(props)
return <Pressable {...props } className={className} />
}Variant autocomplete
If you're using the "Tailwind CSS IntelliSense" Visual Studio Code extension, you can enable autocompletion by adding the following to your settings.json:
{
"tailwindCSS.experimental.classRegex": [
["styled\\([^,)]+,([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
]
}:root CSS Variables
One of the downsides of precompiled styles is static theming and being unable to change values at runtime. NativeWind now allows you to assign a CSS variable as a theme value. These values can be change at runtime using the NativeWindStyleSheet.setVariables function.
NativeWind only supports global CSS variables (those set on
:rootand the special.darkselector
A common pattern is changing CSS variables with the colorScheme, so NativeWind also support .dark as a special selector. It assumes that .dark is being used in a global manner (eg on ). When Dark Mode is active, the :root and .dark variables will be merged together.
CSS variables support being nested inside CSS functions rgba(255, 255, 255, var(--opacity, 1)).
Setting via theme
// tailwind.config.js
module.exports = {
theme: {
variables: {
"--colors-custom-500": "black"
},
darkVariables: {
"--colors-custom-500": "white"
},
}
}
// Compiles to
:root { --custom-500: 'black'; }
.dark { --custom-500: 'white'; }
Setting during runtime
import { NativeWindStyleSheet} from "nativewind"
NativeWindStyleSheet.setVariables({ "--brand-color": "blue" })Setting via plugins
// tailwind.config.js
module.exports = {
plugins: [
plugin(function ({ addBase }) {
addBase({
":root": { "--custom-500": "black" },
".dark": { "--custom-500": "white" },
})
})
]
}
Setting via CSS
// styles.css
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--custom-500: black;
}
.dark {
--custom-500: white;
}Web SSR
On the web, NativeWindStyleSheet.setVariables() will add variables to the root element, which will cause hydration errors. You can use <Html lang="en" style={NativeWindStyleSheet.getSSRStyles()}>
Limitations
NativeWind only implements a subset of the CSS variable specification. The main two differences are
- Can only be set on
:rootor.dark - Does not support multiple values eg
--background: white url(...)
useUnsafeVariable()
NativeWind exposes a new useUnsafeVariable hook which allows your components to react to variable updates. useUnsafeVariable is unsafe because it behaves slightly differently between platforms, especially with units. For example 10vw on native will return a number that is the calculated value of 10vw while on web it returns the string "10vw".
For consistent behaviour across platforms, it is recommend you only use this hook for string variables such as colors.
import { MotiView } from "moti"
import { useVariable } from "nativewind"
function MyView() {
const [brandColorFrom] = useUnsafeVariable("--brand-color-300")
const [brandColorTo] = useUnsafeVariable("--brand-color-500")
return (
<MotiView
from={{ backgroundColor: brandColorFrom }}
animate={{ backgroundColor: brandColorTo }}
exit={{ backgroundColor: brandColorFrom }}
/>
)
}useUnsafeVariable also allows you to set the variable, similar to useState
const [value, setValue] = useUnsafeVariable("--brand-color-300")rem Units
Many developers use rem units to ensure consistent scaling across their application. NativeWind now mimics this behaviour using the CSS variable --rem, which defaults to 14. Developers can change their either by adding a font-size to :root or by calling NativeWindStyleSheet.setVariable({ --rem: <value > })
Inline theme functions
Theme functions like platformSelect previously could only be used with other theme functions. Now they can be used inline with other CSS functions var(--error-color, ${platformColor({ ios: "systemRed", default: "red" }))
RTL Support
The rtl and ltr variants are now supported. Developers can change this value via NativeWindStyleSheet.setDirection() or NativeWindStyleSheet.toggleDirection()
Odd/Even/First/Last
On native, the parent element will need the
parentclassName. This provides the context for these variants to work
The odd/even/first/last variants are now supported for styling children.
Bug Fixes & Improvements
- NativeWind 3.0 styles are properly deduplicated, significantly improving startup time
- A number of caching issues have been fixed
- Fixed edge cases where modifiers and variants did not apply correctly
Experimental CSS support
You can now optionally include a single .css import at your App entrypoint which NativeWind will use as the input for your Tailwind styles. This file needs to contain the TailwindCSS atRules and can also contain additional CSS rules.
Custom CSS is a highly experimental feature that allows developers to write custom CSS classes and use features like @apply without learning Tailwind's plugin system.
NativeWind implements a very small subset of the CSS spec, enough for the supported Tailwind CSS classes to work. This feature is not intended for general use
// global.css
+ @tailwind base;
+ @tailwind components;
+ @tailwind utilities;
.my-class {
@apply text-base text-black
}
// App.tsx
import { Text, View } from "react-native";
+ import "./styles.css";
export function Test() {
return (
<View className="container">
<Text>Hello world!</Text>
</View>
);
}
Activity
marklawlor commentedon Oct 31, 2022
nativewind@nextis now available for public testing, please leave any feedback in this issue.This is a preview release - so don't be surprised if there are some stray bugs. I hope to use your feedback in stabilizing for a stable release.
Docs will be available at https://next.nativewind.dev/, but are currently out of date.
I will be hiding comments of resolved issues so I can keep track of incoming comments.
Known issues
styled()&<StyledComponent />refprop is incorrectly typedfortezhuo commentedon Nov 1, 2022
marklawlor commentedon Nov 1, 2022
fortezhuo commentedon Nov 1, 2022
marklawlor commentedon Nov 1, 2022
fortezhuo commentedon Nov 1, 2022
reanzi commentedon Nov 1, 2022
marklawlor commentedon Nov 1, 2022
marklawlor commentedon Nov 1, 2022
fortezhuo commentedon Nov 1, 2022
reanzi commentedon Nov 1, 2022
EliasGcf commentedon Nov 2, 2022
reanzi commentedon Nov 2, 2022
marklawlor commentedon Nov 2, 2022
153 remaining items
atanaskanchev commentedon Apr 15, 2023
It seems like the project is abandoned, so sad. I've moved over to tamagui...
jamesallain commentedon Apr 18, 2023
He's doing major work on expo-router to make it compatible with nativewind.
Jonatthu commentedon Apr 18, 2023
@atanaskanchev I do not blame you to think that here is the real reason:
expo/router#498
marklawlor commentedon Apr 19, 2023
Hi everyone.
As explained in #464, development is now moving to a new project within the Expo organisation. While this project may be put on hold - the vision for Tailwind in React Native is stronger than ever. More updates coming soon.
pixelkoduje commentedon Jun 26, 2023
The odd class and other child classes - even/odd etc. only work when I add "divide-y-0", the parent class doesn't seem to work. But it works when I add divide-y-0. I am using the latest version as of today. How to use group classes and odd/even etc. properly, as of today?
senghuotlay commentedon Jul 15, 2023
How can i make it work as UI package in turborepo, and i've tried, adding
app/tailwind.config.js
i've even added nativewind to the UI package,
ui/tailwind.config.js
No matter what i've done, the tailwind isn't picking up any styling from my UI package at all
XantreDev commentedon Jul 15, 2023
Every time when you changing tailwind config you need to do
yarn start --reset-cachesenghuotlay commentedon Jul 15, 2023
it's still not working for some reason, it's only the UI package that isn't working. Do u have an example guide that i can follow?
hukpo commentedon Aug 13, 2023
A small tip I found digging in source code
You can use color variables with opacity, just set them right in your tailwind.config.js file
255, 0, 0commas are REQUIREDrgba(var(--primary), <alpha-value>)active:bg-primary/90works nowP.S.
useUnsafeVariableneeds a wrapper to work properly (see the last image)UPDATE: read my comment below to fix issue with variables object and colors in your tailwind config
I was able to support multiple themes using this approach, works like a charm for both Web and Native
@nareshbhatia you probably had the same issue.
yekta commentedon Sep 5, 2023
Even with this setup, I'm still getting the error on native (not web), is there anything else to this? Or do you have a public repo I can check out, that would be very helpful. Spent 2 hours trying to make this work, trying all sorts of combinations 😄
Here is the Tailwind config:
Here is the CSS file:
hukpo commentedon Sep 5, 2023
@yekta Try to JSON.stringify each color in variables object
I found it later because I wasn't using the variables property, but it works
These are all temporary hacks before stable v4 is live
yekta commentedon Sep 5, 2023
That did it, thanks a lot.