react-native-unistyles

repository·main·Indexed 25 days ago

https://github.com/jpudysz/react-native-unistyles

A high-performance styling library for React Native powered by C++ and Nitro Modules. It enables cross-platform style sharing with minimal overhead and no unnecessary re-renders. Features include dynamic keyboard handling via the ime inset, web support through getWebProps, and deep integration with react-native-reanimated via useAnimatedTheme and useAnimatedVariantColor. Compatible with React Compiler and Expo Router web static rendering.

Tokens
67.9K
Snippets
202
Records
367
Agent score
82%

What's inside react-native-unistyles

  1. Introduction to Unistyles

    main

    Unistyles is a cross-platform styling library for React Native that allows you to share up to 100% of your styles across all platforms. It acts as a superset of the standard React Native StyleSheet, providing enhanced capabilities while maintaining a familiar API.

    Key features include:

    • Performance: Uses pure JSI bindings and C++ to guarantee no re-renders across the app without relying on hooks or context.
    • Compatibility: Does not pollute the native view hierarchy, allowing it to be used with any component.
    • Consistency: Includes a cross-platform parser written in C++ for consistent output.
    • Type Safety: Leverages Nitro Modules for strongly typed operations.
    • Enhanced Stylesheets: Transforms standard StyleSheets into enhanced versions that can access themes and platform-specific values.
  2. How Unistyles 3.0 updates styles without re-renders

    main

    Unlike traditional styling libraries that require a full React re-render to reflect style changes, Unistyles 3.0 uses a C++ engine to perform atomic updates.

    When a relevant event occurs (such as a theme change, orientation change, or accessibility setting update), Unistyles:

    1. Scans the StyleSheetRegistry to find affected styles.
    2. Uses the ShadowRegistry to identify which ShadowNode is bound to those styles.
    3. Translates updates into atomic ShadowTree instructions.
    4. Updates the ShadowTree directly from C++, meaning your React components do not need to re-render to reflect the new styles.
  3. Understand Unistyles Babel plugin responsibilities

    main

    The Babel plugin performs four key tasks to enable Unistyles 3.0:

    1. Detecting StyleSheet dependencies: It analyzes StyleSheet.create calls to identify if styles depend on theme or miniRuntime (e.g., insets, fontScale). This ensures only relevant styles recalculate when dependencies change.
      • Limitation: It does not support moving functions/arrow functions out of StyleSheet.create or reassigning theme/rt to other variables.
    2. Attaching unique IDs: It assigns unique IDs to StyleSheet objects to enable reliable Hot Module Replacement (HMR) during development.
    3. Component factory (ref borrowing): It transforms components (like View, Pressable, or Image) to borrow the ref prop. This allows Unistyles to connect a ShadowNode to the native view without polluting the native view hierarchy.
    4. Creating scopes for stateless variants: When using useVariants, the plugin creates a local scope for the stylesheet. This allows nested calls to useVariants to exist without affecting other components using the same stylesheet.
  4. Understand Unistyles Web Styles

    main

    In Unistyles 3.0, the web implementation is independent of React Native Web. It uses a custom web parser that converts StyleSheet definitions directly into CSS.

    Key behaviors:

    • Class Generation: The parser generates unique classNames for styles and assigns them to DOM elements.
    • Media Queries: Media queries are automatically generated based on your defined breakpoints, which optimizes performance by avoiding recalculations on every resize.
    • CSS Output: Shared properties (like flex: 1) are consolidated into single classes, while responsive properties (like fontSize with breakpoints) are wrapped in @media queries.
  5. Handle Edge-to-Edge layout on Android with rt.insets

    main

    Unistyles v3 enforces edge-to-edge layout on Android. Instead of using react-native-safe-area-context, use rt.insets within your StyleSheet.create callback to handle status bar and navigation bar insets accurately.

    const styles = StyleSheet.create((theme, rt) => ({
      container: {
        flex: 1,
        paddingTop: rt.insets.top,
        paddingBottom: rt.insets.bottom
      }
    }))
  6. Set up TypeScript for Unistyles themes and breakpoints

    main

    To enable autocomplete for theme properties and breakpoint keys, declare the UnistylesThemes and UnistylesBreakpoints interfaces within the react-native-unistyles module in a declaration file (e.g., unistyles.d.ts).

    // unistyles.d.ts (or in your config file)
    import { lightTheme, darkTheme } from './themes'
    import { breakpoints } from './breakpoints'
    
    type AppThemes = {
      light: typeof lightTheme
      dark: typeof darkTheme
    }
    
    declare module 'react-native-unistyles' {
      export interface UnistylesThemes extends AppThemes {}
      export interface UnistylesBreakpoints extends typeof breakpoints {}
    }
  7. Replace standard StyleSheet with Unistyles StyleSheet

    main

    To use Unistyles for styling, replace the standard react-native StyleSheet import with react-native-unistyles.

    When using Unistyles, StyleSheet.create accepts a callback function that receives the theme object. This function is automatically re-invoked whenever the theme changes, ensuring styles stay synchronized with the current theme state.

    import { StyleSheet } from 'react-native-unistyles';
    
    const styles = StyleSheet.create(theme => ({
      container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
      },
    }));
  8. Modify App Entry Point for Unistyles

    main

    To ensure themes and breakpoints are available before any application code runs, you must configure Unistyles at the very start of your app's lifecycle.

    1. Update package.json to change the main entry point from expo-router/entry to index.ts.
    2. Create an index.ts file in your project root that imports the standard Expo Router entry and your Unistyles configuration file.
    // package.json
    {
      "main": "index.ts"
    }
    // index.ts
    import 'expo-router/entry'
    import './unistyles'
  9. Register custom breakpoints

    main

    Breakpoints are key/value pairs that define screen size boundaries. To register them, create a constant object where the first breakpoint must start with 0 to enable CSS-like cascading behavior.

    If using TypeScript, you must extend the UnistylesBreakpoints interface to ensure type safety for your custom keys.

    // 1. Define breakpoints
    const breakpoints = {
        xs: 0,
        sm: 576,
        md: 768,
        lg: 992,
        xl: 1200,
        superLarge: 2000,
        tvLike: 4000
    } as const
    
    // 2. TypeScript augmentation
    type AppBreakpoints = typeof breakpoints
    
    declare module 'react-native-unistyles' {
      export interface UnistylesBreakpoints extends AppBreakpoints {}
    }
    
    // 3. Register via StyleSheet.configure
    import { StyleSheet } from 'react-native-unistyles'
    
    StyleSheet.configure({
        breakpoints
    })
  10. Provide Unistyles documentation to LLMs

    main

    If you are using an LLM (Large Language Model) to assist with Unistyles 3.0 development, you can provide it with auto-generated documentation files to improve its context and accuracy. Unistyles provides three versions of documentation optimized for different LLM consumption needs:

    • llms.txt: Standard documentation format.
    • short documentation: A condensed version for smaller context windows.
    • full documentation: A comprehensive version for deep context.

    You can point your LLM or AI coding agent to these URLs to feed it the relevant Unistyles 3.0 knowledge.

  11. Integrate Unistyles with Reanimated

    main

    To use Unistyles values inside Reanimated worklets, import hooks from react-native-unistyles/reanimated.

    useAnimatedTheme()

    Returns a shared value containing the current theme. CRITICAL: Never spread Unistyles styles and Reanimated styles together in a single object. Always use an array: style={[styles.container, animatedStyle]}.

    useAnimatedVariantColor(style, colorKey)

    Animates color changes when variants change. Requirements:

    • The style must be created by Unistyles.
    • The style must have variants.
    • The colorKey must contain the string "color" (case-insensitive).
    import { useAnimatedTheme, useAnimatedVariantColor } from 'react-native-unistyles/reanimated'
    
    // Example: useAnimatedTheme
    const animatedTheme = useAnimatedTheme()
    const animatedStyle = useAnimatedStyle(() => ({
      backgroundColor: animatedTheme.value.colors.background
    }))
    <Animated.View style={[styles.container, animatedStyle]} />
    
    // Example: useAnimatedVariantColor
    const derivedColor = useAnimatedVariantColor(styles.button, 'backgroundColor')
    const animatedStyle = useAnimatedStyle(() => ({
      backgroundColor: derivedColor.value
    }))
    <Animated.View style={[styles.button, animatedStyle]} />
  12. Prerequisites for Unistyles v3

    main

    Before migrating to Unistyles v3, ensure your environment meets these mandatory requirements:

    • React Native: 0.78.0+ with New Architecture enabled.
    • React: 19+
    • Native Dependencies: react-native-nitro-modules and react-native-edge-to-edge (for Android edge-to-edge insets).
    • Expo: SDK 53+ (requires Dev Client or Prebuild; not compatible with Expo Go).
    • iOS: Xcode 16+.