Babel plugin
Unistyles 3.0 relies heavily on the Babel plugin, which helps convert your code in a way that allows binding the ShadowNode
with Unistyle
. Before reading this guide, make sure to check the Look under the hood guide.
Our golden rule is to never introduce any component that could pollute your native view hierarchy. In other words, if you use a View
, it will be rendered as-is in the native view hierarchy.
Let’s discuss the responsibilities of the Babel plugin:
1. Detecting StyleSheet dependencies
Each StyleSheet
is different. One might rely on a theme
, another on miniRuntime
, and so on.
The same applies to styles
. Each style depends on different things. For example, you can wrap your app in a View
that safeguards your app from rendering behind the notch or navigation bar.
Another style might be used in your Typography
component and provides text color based on the apps’ theme.
Should the Typography
style re-calculate on an insets
change? Or should the View
that relies on insets re-render on a theme change?
We don’t think that’s a good idea. The first responsibility of the Babel plugin is to detect all dependencies in your StyleSheet
. This ensures that only the relevant styles are recalculated when necessary.
// Babel: depends on themeconst stylesheet = StyleSheet.create(theme => ({ container: { // Babel: depends on theme backgroundColor: theme.colors.background }, text: { // Babel: static (no dependencies) fontSize: 12 }}))
// Babel: depends on theme and miniRuntimeconst stylesheet = StyleSheet.create((theme, rt) => ({ container: { // Babel: depends on theme and insets paddingTop: rt.insets.top, paddingBottom: rt.insets.bottom, backgroundColor: theme.colors.background }, text: (fontSize: number) => ({ // Babel: depends on theme color: theme.colors.text, // Babel: depends on fontScale fontSize: rt.fontScale >= 3 ? fontSize * 1.5 : fontSize * 0.8 })}))
2. Attaching unique id to each StyleSheet
This helps us identify your StyleSheet
while you’re developing your app and trigger multiple hot-reloads
. Such identification is required to swap your StyleSheet
with another one, ensuring that you get up-to-date values during reloads.
This feature does not affect your app in production, as the bundle never reloads in that environment.
3. Modifying your component style
prop
Each Unistyle
(C++ HybridObject) has an attached C++
state. This state can be lost when using the spread operator, which is why Unistyles safeguards against that.
If we try to read the style on C++ side without its associated state, an error will be thrown: Unistyle is not bound!
.
However, this doesn’t mean you can’t copy or spread your styles. It is still possible to do so, but it requires the Babel plugin, which converts your styles into an array format.
// 😩 ouch, you have just removed C++ stateconst mergedStyles = { ...styles.container, ...styles.text}
// 💥 Unistyle is not bound!<View style={mergedStyles} />
// 😎 you're safeconst mergedStyles = [ styles.container, styles.text]
// 😇 we can read the state<View style={mergedStyles} />
Or with babel plugin:
<View style={{...styles.container, ...styles.text}} />
// 😇 will be replaced with<View style={[styles.container, styles.text]} />
4. Component factory
This is the most crucial part—without it, Unistyles won’t be able to update your views from C++.
In the early versions of Unistyles 3.0, we tried solving this problem by using the ref
prop, but it wasn’t reliable enough.
Many developers use different style syntaxes, making it impossible to support all of them.
Instead, we decided to leave the user’s ref
as is and transfer the implementation from Babel to our component factory.
This way we have more control and we have an unified way of registering your ShadowNodes
.
The component factory is a function that takes your component and renders it with an overridden ref
prop:
const factory = Component => <Component ref={someMagic✨} {...props} />;
Let’s go through some examples so you can better understand how this works:
import { View } from 'react-native'
const ref = useRef()
<View ref={ref} />
import { View } from 'react-native-unistyles/src/components/native/View'
const ref = useRef()
// no changes<View ref={ref} />
We also support other components to extract ShadowNode
from them:
import { Pressable, Image } from 'react-native'
<Pressable ref={ref => { doSomething(ref) }} onPress={() => {}}/><Image source={require('./image.png')} style={styles.image} ref={ref2} />
import { Pressable } from 'react-native-unistyles/components/native/Pressable'import { Image } from 'react-native-unistyles/components/native/Image'
// no changes<Pressable ref={ref => { doSomething(ref) }} onPress={() => {}}/><Image source={require('./image.png')} style={styles.image} ref={ref2} />
Summary
That’s it! We hope you enjoy the DX of Unistyles 3.0 with the help of the Babel plugin. If you encounter any Babel issues, we’re ready to tackle them and resolve them with priority!