v3 - nativewind@next
See original GitHub issueNativeWind 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
Animated
components. 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
aspect
with 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
:root
and the special.dark
selector
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 <body>). 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
:root
or.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
parent
className. 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>
);
}
Issue Analytics
- State:
- Created a year ago
- Reactions:14
- Comments:132 (57 by maintainers)
nativewind@next
is 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 />
ref
prop is incorrectly typed3.0.0-next.33
has been released 🎉Fixes
styled()
props
not settingclassName
correctly @JonatthuFeatures:
New function
NativeWindStyleSheet.getSSRStyles()
. This returns a style object of the CSS variables set during an SSR render.