Dear NativeWind Community,
I'm happy to announce the release of NativeWind v4! This release is the culmination of months of work and I'm excited to share it with you. Previously NativeWind focused on static styling, simply changing className
->style
at build time. NativeWind v4 is a complete rewrite, and now supports highly dynamic styles, allowing you to use Tailwind CSS's full feature set.
The Updated Architecture
NativeWind v4 has transitioned away from using a Babel plugin to a jsxImportSource
transform. In the older architecture, Babel wrapped every component with a className
prop in the StyledComponent
wrapper (or you manually wrapped the component using styled()
) and converted className
->style
. With the jsxImportSource
transform, only native components need to be wrapped (<View/>
,<Text/>
, etc). This has has major advantages of:
- The
className
prop can accessed inside your components. - NativeWind will wrap less components and generally only components which are leaf nodes in the render tree
Preserving the className
prop fixes the biggest limitation and source of confusion with NativeWind, and allows you to use 3rd party className
management libraries (tailwind-variants
,classnames
,clsx
,cva
,etc)
// There is no need to wrap this component! `className` is accessible inside the component!
export function MyText({ className, ...props }: TextProps) {
return <Text className={`text-black ${className}`} {...props} />;
}
New Features
CSS Variables
NativeWind has been updated to include support for CSS Custom Properties, commonly known as CSS Variables.
// You can define them as a theme value
module.exports = {
theme: {
extend: [
colors: {
brand: "var(--brand-color)"
}
]
},
}
You can define them inline
import { vars } from "nativewind";
<View style={vars({ "--brand-color": "red" })}>
<Text className="text-brand">Red text!</Text>
</View>;
Part of your theme
module.exports = {
plugins: [
plugin(function ({ addBase }) {
addBase({
":root": { "--brand-color": "red" },
});
}),
],
};
Or directly in your CSS
/* in CSS */
:root {
--my-brand-color: red;
}
/* including dark mode! */
@media (prefers-color-scheme: dark) {
:root {
--my-brand-color: blue;
}
}
Animations (experimental):
NativeWind adds experimental support for Tailwind CSS animation classes, including custom key-frame animations defined in CSS.
The animation functionality is provided by the widely-used react-native-reanimated
package, which integrates seamlessly with NativeWind without the need for additional configuration. Just apply an animation style, and NativeWind takes care of the rest.
// Use an existing animation class
<View className="animation-bounce" />
// Or define a custom animation in your .css
@keyframes example {
from { background-color: red; }
to { background-color: yellow; }
}
.my-animation {
animation-name: example;
animation-duration: 4s;
}
<View className="my-animation" />
There is no need to use
Animated.View
orAnimated.Text
, NativeWind will automatically create animated version of your components.
Transitions (experimental):
NativeWind adds experimental support for Tailwind CSS transition classes.
// The color will transition over 150ms when the color scheme changes
<Text className="transition-colors text-black dark:text-white" />
Transitions are dynamic, and work with both Tailwind CSS and inline styles.
Tailwind Groups and parent state modifiers
NativeWind v4 now supports the group
and group/<name>
syntax.
https://tailwindcss.com/docs/hover-focus-and-other-states#differentiating-nested-groups
Container Queries
Container queries enable you to apply styles to an element based on the size of the element's container, making them extremely ideal for mobile styling. While technically not part of Tailwind CSS, they are available via the official plugin (TailwindCSS Container Queries)[https://github.com/tailwindlabs/tailwindcss-container-queries].
<View class="@container">
<Text class="@lg:underline">
<!-- This text will be underlined when the container is larger than `32rem` -->
</Text>
</View>
A subset of the Container Query spec is also available within your CSS
// The @container atRule with media based queries
@container (min-width: 700px) {
.my-view {
}
}
// Named container contexts
.my-container {
container-name: sidebar;
}
@container sidebar (min-width: 700px) {
.my-view {
}
}
container-type
and style-based container queries are not supported.
Improved compilation
NativeWind v4 improves hot-reloading, including hot reloading when you make change to your tailwind.config.js
theme!. This greatly improves the development experience and allows you to quickly iterate on designs made with NativeWind.
Additionally, the style compiler has be rewritten using lightningcss and should be significantly faster than v2.
rem
Support
NativeWind v4 includes scaling support for the rem
unit. By default NativeWind will inline the rem
value, replacing rem
with px
at build time. This greatly improves performance, but can be disabled if you need to dynamically change the rem
value at runtime. More information
export default withNativeWind(config, {
input: "global.css",
inlineNativeRem: false // Disable rem inlining
// OR
inlineNativeRem: 16 // Set a custom rem value
})
Improved support for React Native core components (native only)
NativeWind v4 adds more sensible defaults for the React Native core components. These includes automatically mapping some styles to props.
// You write
<ActivityIndicator className="bg-black text-white" />
// ❌ NativeWind v2
<ActivityIndicator style={{ backgroundColor: "rgba(0, 0, 0, 1)", color: "rgba(255, 255, 255, 1)" }}/>
// ✅ NativeWind v4
<ActivityIndicator color="rgba(255, 255, 255, 1)" style={{ backgroundColor: "rgba(0, 0, 0, 1)" }}/>
Theme Functions
The theme functions have been improved and now support nested functions.
import { platformSelect, platformColor, pixelRatioSelect, hairlineWidth } from "nativewind/theme"
module.exports = {
theme: {
extend: {
colors: {
brand: platformSelect({
ios: platformColor('label'),
android: platformColor('?android:attr/textColor')
default: "var(--brand-color, black)
})
}
borderWidth: {
"hw": pixelRatioSelect({
1: hairlineWidth(),
1.5: 1,
default: hairlineWidth()
})
}
}
}
}
React 18 Improvements
React 18 has significantly altered our approach to React applications. New features like React Server Components and the Suspense APIs necessitated a more strategic viewpoint from library authors. In response to this, NativeWind has been rewritten to ensure compatibility with Suspense APIs will work with React Server Components on web.
React Native Web improvements
React Native Web has upcoming "compiler-less" mode which removes the built in CSS StyleSheet compiler, allows for smaller/faster web applications. As NativeWind already pre-builds your CSS, you'll be able to take advantage of this from day 1.
Custom CSS (experimental)
You may have noticed many of our examples include raw CSS. NativeWind now supports custom CSS, allowing you to use Tailwind CSS alongside your own custom styles. Note that we currently only support a limited subset of CSS rules and properties. Further documentation is on the way.
@tailwind base;
@tailwind components;
@tailwind utilities;
.my-class {
@apply text-base text-black
}
// Media queries are supported
@media (prefers-color-scheme: dark) {
.my-class {
@apply text-base text-white
}
}
import { Text, View } from "react-native";
export function Test() {
return (
<View className="container">
<Text className="my-class">Hello world!</Text>
</View>
);
}
Breaking Changes from v2
The introduction of a new architecture inevitably brings some breaking changes.
Removal of styled()
There are two reasons for the removal of styled()
. As explained above NativeWind no longer needs to wrap every component removing the primary need for the styled()
wrapper. While the new APIs enablePropRemap
/enableCSSInterop
serve similar purposes, you should be using them significantly less than styled()
.
Secondly, NativeWind v4 is making a philosophical choice to be a styling library, not a component library. The difference is small, but there is a important distinction in terms of API coverage. styled()
allowed you to create components because NativeWind incompatible with popular 3rd party component/variant libraries and we believed that restriction greatly reduced the development experience when using NativeWind. NativeWind v4 has now "fixed" className
, removing that restriction and allowing you to use which ever library best fits your use-case.
NativeWind does not offer a migration path for components using styled()
, however the library tw-classed offers a very similar API and should provide you with a straight forward migration (note: You can now skip step 2)
import { Text as RNText } from "react-native";
import { classed } from "@tw-classed/react";
export const Text = classed(RNText, "text-black", {
variants: {
color: {
blue: "text-blue-500",
green: "text-green-500",
},
},
});
const App = () => {
return <Text color="blue">Hello, tw-classed!</Text>;
};
CSS Specificity
NativeWind has change its specificity algorithm. You can read more about it here: https://www.nativewind.dev/core-concepts/style-specificity
Base Scaling Modifications
NativeWind now processed the rem
unit, a significant change affecting all rem
based styles. Previously, NativeWind matched Tailwind CSS documentation's scaling, replacing rem
values with their px
equivalents. Now, the default rem
value is set to 14, aligning with <Text />
default. As a result, your app might appear smaller due to the scale change from the static 16px to 14. To revert to the previous behavior, set the inlineNativeRem
option in your withNativeWind
config:
jsxCopy code
// metro.config.js
export default withNativeWind(config, {
input: "global.css",
inlineNativeRem: 16 // Modify this
})
gap-
polyfill has been removed
gap
now compiles to the native columnGap
and rowGap
styles. Previously NativeWind tried to mimic this behavior using margin
and the removal of the polyfill may affect your layouts.
useColorScheme()
setColorScheme
and toggleColorScheme
will throw an error unless darkMode: 'dark'
is set in your tailwind.config.js
.
Removal of group-isolate
and parent
With the new support for Tailwind groups, group-isolate
and parent
are superseded. This feature necessitates Tailwind CSS 3.2.
https://tailwindcss.com/docs/hover-focus-and-other-states#differentiating-nested-groups
divide-
and spacing-
temporary unavailable
The divide-
and spacing-
utilities will be unavailable at the launch of v4 and will be re-added in a future version. Previously these utilities were poly-filled and we are exploring better ways to re-implement them.
NativeWindStyleSheet
renamed to StyleSheet
The StyleSheet exported from NativeWind now extends StyleSheet
from React Native and can be used a drop in replacement.
New fontFamily
defaults
NativeWind v4 adds new defaults for the font family class names. You can override these defaults in your tailwind.config.js
. We
import { platformSelect } from "nativewind/theme"
module.exports = {
theme: {
fontFamily: {
sans: platformSelect({
android: 'san-serif',
ios: 'system font',
web: 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"'
}),
serif: platformSelect({
android: 'serif',
ios: 'Georgia'
web: 'ui-serif, Georgia, Cambria, "Times New Roman", Times, serif'
}),
mono: platformSelect({
android: 'mono',
ios: 'Courier New'
web: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
}),
}
}
}
pixelRatio
/ fontScale()
pixelRatio
and fontScale
have been updated so they now return the respective value. If a number is passed as an argument, it will be multiplied by the value.
pixelRadio(2) = PixelRatio.get() * 2
There are two new functions pixelRatioSelect
and fontScaleSelect
. These work similar to Platform.select
pixelRatioSelect({
2: '1.2rem'
default: '1rem'
})
Miscellaneous Updates
aspect
no longer uses a polyfills and adopt native styling.- NativeWind no longer exports a PostCSS plugin. Use the Tailwind CLI if you wish to generate the
.css
file manually. NativeWindStyleSheet.setOutput()
has been removed. Output is now determined by your Metro targeted platform.border-0.5
has been renamed toborder-hairline
Breaking changes from v3 beta
If you were using the v3 beta, these are some additional breaking changes
Variant API Discontinuation
We had introduced variant support for styled()
in the cancelled v3 beta, based on the class-variance-authority
library. We recommend users to shift to (tailwind-variants
)[https://www.tailwind-variants.org/], enhancing control over styling and aligning NativeWind with evolving coding practices.
setVariables() has been removed
Simply add the variables to a component’s style tag using the new vars()
function. See the New Features for more information.
Misc
useUnsafeVariable
has been removed. UseuseUnstableNativeVariables
instead.setDirection()
has been removed. UseI18nManager.forceRTL
instead.**odd/even/first/last
: These modifiers are temporary unavailable. They will be added in a future version.NativeWindStyleSheet.getSSRStyles()
has been removed and is no longer required.
New API
remapProps
propRemap
accepts a Component
as the first argument and a mapping as the second. The mapping is in the format { [existing prop]: [new prop] | true }
, and it returns a typed version of the component.
An example of how NativeWind maps <FlatList />
is:
remapProps(FlatList, {
style: "className",
ListFooterComponentStyle: "ListFooterComponentClassName",
ListHeaderComponentStyle: "ListHeaderComponentClassName",
columnWrapperStyle: "columnWrapperClassName",
contentContainerStyle: "contentContainerClassName",
});
// Now you can use FlatList with the added props
<FlatList className="w-10" ListHeaderComponentClassName="bg-black" />;
remapProps
is a lightweight wrapper which doesn't generate any styles. Instead it converts Tailwind CSS strings to OpaqueStyleTokens
(readonly empty objects). These tokens should be treat just any other other style object, and once passed to a component tagged with remapProps
, they will be converted into styles.
(See the documentation for more information)[https://www.nativewind.dev/api/remap-props]
cssInterop(component, mapping)
cssInterop
signals to NativeWind that a specific component should be treated as a primitive. On this component, it sets up the dynamic style logic.
Before using cssInterop
you should could consider if remapProps
would be more suitable. The main reasons to use cssInterop
are:
- The component renders a Native Component
- Moving a style property to an prop
- Note: If this is not a 3rd party component, you should consider simply using the style prop as this will not work on web.
(See the documentation for more information)[https://www.nativewind.dev/api/css-interop]
vars()
vars()
allow you can create inline CSS Variables that will be shared by React Context to all children.