question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

v3 - nativewind@next

See original GitHub issue

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 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:open
  • Created a year ago
  • Reactions:14
  • Comments:132 (57 by maintainers)

github_iconTop GitHub Comments

6reactions
marklawlorcommented, Nov 15, 2022

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

  • Docs are incorrect/missing
  • Babel transform allow/block module options are not implemented
  • styled() & <StyledComponent /> ref prop is incorrectly typed
4reactions
marklawlorcommented, Dec 1, 2022

3.0.0-next.33 has been released 🎉

Fixes

Features:

New function NativeWindStyleSheet.getSSRStyles(). This returns a style object of the CSS variables set during an SSR render.

<Html lang="en" style={NativeWindStyleSheet.getSSRStyles()}>
Read more comments on GitHub >

github_iconTop Results From Across the Web

chengyinwu/V3: An Extensible Framework for ... - GitHub
V3 is a new and extensible framework for hardware verification and debugging researches on both Boolean-level and word-level designs.
Read more >
ProAudioDSP DSM V3 - Plugin Alliance
Pro Audio DSP DSM V3 is a unique, spectral multiband processor. Capture the characteristics of any source, and apply it to any other...
Read more >
Important information when upgrading to v3.2
v3.2 of the Argon firmware is now available. In order to install this firmware version, users must upgrade their copy of the MODALapp...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found