Unistyles 3.0 Patterns
SkillAI & modelsUnistyles 3.0 styling - C++ powered StyleSheet superset with zero re-renders, theming, breakpoints, variants, dynamic functions, runtime values
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Unistyles 3.0 Patterns skill
What this skill tells your AI
The instructions your AI receives, as published by agents-inc/skills in src/skills/mobile-styling-unistyles/SKILL.md and read by ahel’s review.
Quick Guide: Unistyles 3.0 is a StyleSheet superset powered by Nitro Modules (C++/JSI). Import
StyleSheetfromreact-native-unistylesinstead ofreact-native-- same API, but with themes, breakpoints, variants, dynamic functions, and runtime values. Zero re-renders: styles update via the Shadow Tree, not React state. Configure withStyleSheet.configure()before anyStyleSheet.create(). Never spread styles ({...a, ...b}) -- use array syntax ([a, b]). Requires New Architecture (RN 0.78+).
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST import StyleSheet from react-native-unistyles, NOT from react-native -- the Unistyles version is a superset that enables all features)
(You MUST call StyleSheet.configure() BEFORE any StyleSheet.create() -- configure in your entry file before importing components)
(You MUST use array syntax [styles.a, styles.b] for merging styles -- NEVER spread {...styles.a, ...styles.b} as it destroys C++ state)
(You MUST NOT use useUnistyles hook in regular components -- it forces full re-renders, defeating Unistyles' zero-render architecture)
(You MUST pass only serializable arguments to dynamic functions -- strings, numbers, booleans, arrays, objects (no functions or components))
</critical_requirements>
Auto-detection: Unistyles, react-native-unistyles, StyleSheet.configure, UnistylesRuntime, UnistylesThemes, UnistylesBreakpoints, useVariants, compoundVariants, ScopedTheme, withUnistyles, useUnistyles, miniRuntime, rt.insets, rt.screen, mq.only, Display, Hide
When to use:
- Styling React Native apps that need dynamic theming (light/dark or custom themes)
- Building responsive layouts with breakpoints and media queries across mobile and web
- Creating reusable component variants (size, color, state) without conditional logic
- Accessing runtime device values (insets, screen size, font scale) inside stylesheets
- Migrating from Unistyles 2.x to 3.0
When NOT to use:
- Apps that cannot use the New Architecture (requires RN 0.78+, Fabric)
- Expo Go apps (requires development builds with native modules)
- Minimal apps with no theme switching or responsive needs (plain StyleSheet suffices)
- Apps using a utility-class approach (consider a utility-class styling solution instead)
Key patterns covered:
- StyleSheet.configure: themes, breakpoints, settings registration
- StyleSheet.create with theme and miniRuntime (rt) access
- Variants and compound variants for reusable component styles
- Dynamic functions with serializable parameters
- Breakpoints, media queries, and Display/Hide components
- Runtime values: insets, screen dimensions, font scale, color scheme
- Scoped themes and adaptive themes
- withUnistyles for third-party component integration
- Style merging with array syntax (never spread)
Detailed Resources:
- examples/core.md - StyleSheet setup, theme access, dynamic functions, style merging
- examples/theming.md - Theme configuration, adaptive themes, scoped themes, runtime switching
- examples/responsive.md - Breakpoints, media queries, Display/Hide, runtime values
- examples/variants.md - Variants, compound variants, boolean variants, component props pattern
- reference.md - API quick reference, decision frameworks, v2-to-v3 migration
Philosophy
Unistyles 3.0 is a StyleSheet superset -- if you know React Native's StyleSheet.create, you know 80% of Unistyles. The remaining 20% is what makes it powerful: themes, responsive breakpoints, variants, and runtime values, all managed in C++ via JSI with zero React re-renders.
How it works:
- Babel plugin analyzes StyleSheets at build time, detecting dependencies (theme, runtime, breakpoints)
- C++ core reconstructs StyleSheets natively and tracks which styles depend on what
- Shadow Tree updates bypass React entirely -- when a theme changes or the device rotates, only the affected ShadowNodes update their styles directly
Core principles:
- Zero re-renders -- No hooks, no context, no state updates for style changes. The C++ core updates the Shadow Tree directly.
- StyleSheet superset -- Same API as React Native's StyleSheet. Replace the import, keep your code.
- Type-safe themes -- TypeScript declarations ensure full autocompletion for theme properties.
- Selective recalculation -- Only styles that depend on a changed value (theme, breakpoint, inset) are recalculated.
- Cross-platform -- Same styles work on iOS, Android, and web (with automatic CSS class generation for web).
When Unistyles adds value over plain StyleSheet:
- Multiple themes (dark/light/custom) with instant switching
- Responsive layouts that adapt to screen size, orientation, or device type
- Component variants (primary/secondary, small/large) without conditional style logic
- Runtime-dependent styles (safe area insets, font scale, keyboard height)
Core Patterns
Pattern 1: StyleSheet.create with Theme and Runtime
Replace react-native import with react-native-unistyles. The create function accepts a callback with theme and rt (miniRuntime) for dynamic styles. Static styles (no theme/runtime) work identically to plain StyleSheet.
import { StyleSheet } from "react-native-unistyles";
const styles = StyleSheet.create((theme, rt) => ({
container: {
flex: 1,
backgroundColor: theme.colors.background,
paddingTop: rt.insets.top,
},
text: {
color: theme.colors.typography,
fontSize: rt.fontScale * 16,
},
// Static styles work exactly like plain StyleSheet
separator: {
height: StyleSheet.hairlineWidth,
backgroundColor: "#ccc",
},
}));
Why good: theme and rt are injected by the C++ core -- no hooks needed, no re-renders when theme or device values change
See examples/core.md for static vs themed vs runtime StyleSheets and the full miniRuntime property list.
Pattern 2: Variants and Compound Variants
Define style variations inside variants -- then select them with styles.useVariants(). Compound variants apply styles when multiple variant conditions are met simultaneously.
const styles = StyleSheet.create((theme) => ({
button: {
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 8,
variants: {
color: {
primary: { backgroundColor: theme.colors.primary },
secondary: { backgroundColor: theme.colors.secondary },
},
size: {
sm: { paddingHorizontal: 8, paddingVertical: 4 },
lg: { paddingHorizontal: 24, paddingVertical: 12 },
},
},
compoundVariants: [
{
color: "primary",
size: "lg",
styles: { borderWidth: 2, borderColor: theme.colors.accent },
},
],
},
}));
// In component -- call useVariants to select active variants
styles.useVariants({ color: "primary", size: "lg" });
Why good: eliminates conditional style objects, compound variants reduce complex if/else chains, TypeScript infers valid variant combinations
See examples/variants.md for boolean variants, default variants, the component props pattern with UnistylesVariants, and multi-style variants.
Pattern 3: Dynamic Functions
When styles depend on component-level values (not just theme/runtime), use dynamic functions. Arguments must be serializable (strings, numbers, booleans, arrays, objects).
const styles = StyleSheet.create((theme) => ({
card: (isHighlighted: boolean, index: number) => ({
backgroundColor: isHighlighted
? theme.colors.highlight
: theme.colors.surface,
opacity: index === 0 ? 1 : 0.8,
}),
}));
// In JSX -- call the function with arguments
<View style={styles.card(isHighlighted, index)} />
Why good: serializable arguments pass to C++ for native recalculation, full TypeScript inference on parameters
See examples/core.md for dynamic function patterns and the serializable constraint.
Pattern 4: Breakpoints and Media Queries
Define breakpoints in StyleSheet.configure, then use breakpoint objects or the mq utility in styles. At least one breakpoint must start at 0.
// In style definitions -- breakpoint object syntax
const styles = StyleSheet.create((theme) => ({
container: {
flexDirection: {
xs: "column",
md: "row",
},
padding: {
xs: 8,
sm: 16,
lg: 24,
},
},
}));
// Media query syntax for precise ranges
import { mq } from "react-native-unistyles";
const styles = StyleSheet.create(() => ({
sidebar: {
display: {
[mq.only.width(0, 768)]: "none",
[mq.only.width(768)]: "flex",
},
},
}));
Why good: breakpoints cascade like CSS (xs applies until sm overrides), mq provides precise range control, web automatically generates CSS media queries
See examples/responsive.md for Display/Hide components, landscape/portrait breakpoints, and mixing breakpoints with mq.
Pattern 5: Style Merging (Array Syntax)
Never spread Unistyles objects. Spreading destroys the C++ state that tracks dependencies. Use React Native's array syntax for merging.
// CORRECT -- array syntax preserves C++ state
<View style={[styles.container, styles.overlay]} />
<View style={[styles.card, isFocused && styles.focused]} />
// WRONG -- spreading destroys C++ state
<View style={{ ...styles.container, ...styles.overlay }} />
Why bad (spread): spreading removes the C++ state Unistyles attaches, forcing it to restore state in unpredictable order; triggers dev-mode warnings
See examples/core.md for merging patterns and conditional style application.
Pattern 6: withUnistyles for Third-Party Components
Third-party components that don't expose native views via ref cannot benefit from Unistyles' Shadow Tree updates. Wrap them with withUnistyles to subscribe to theme and runtime changes.
import { withUnistyles } from "react-native-unistyles";
import { BlurHash } from "react-native-blurhash";
// Static mappings -- re-renders only when dependencies change
const UniBlurHash = withUnistyles(BlurHash, (theme) => ({
color: theme.colors.tint,
}));
// Dynamic mappings via uniProps
<UniBlurHash
uniProps={(theme, rt) => ({
color: rt.colorScheme === "dark"
? theme.colors.darkTint
: theme.colors.lightTint,
})}
/>
Why good: component re-renders only when its dependencies change, not on every theme/runtime update
When to use: only for third-party components that don't work with standard Unistyles styles. Regular React Native components (View, Text, Pressable) work without it.
See examples/core.md for uniProps priority and when to choose withUnistyles vs useUnistyles.
<decision_framework>
Decision Framework
Choosing the Right Styling Approach
Does the component need theme colors or runtime values?
|-- NO -> Plain StyleSheet.create (static object, no callback)
+-- YES -> StyleSheet.create((theme, rt) => ...)
|
Does it also need component-local values (props, state)?
|-- YES -> Dynamic function: style: (arg) => ({ ... })
+-- NO -> Static theme/runtime access is enough
Does the style have multiple visual variants (size, color, state)?
|-- YES -> Use variants {} inside the style
| |
| Do combinations of variants need special treatment?
| +-- YES -> Add compoundVariants []
+-- NO -> Regular style properties
Is this a third-party component that doesn't work with Unistyles styles?
|-- Try withUnistyles first (no re-renders)
+-- Only if that fails -> useUnistyles hook (causes re-renders)
Responsive: Breakpoints vs Media Queries vs Display/Hide
| Need | Solution |
|---|---|
| Simple per-breakpoint values | Breakpoint object { xs: 8, md: 16 } |
| Precise pixel ranges | mq.only.width(0, 500) |
| Show/hide entire components | <Display mq={...}> / <Hide mq={...}> |
| Orientation-specific styles | Built-in portrait / landscape breakpoints |
</decision_framework>
<red_flags>
RED FLAGS
High Priority Issues:
- Spreading styles
{...styles.a, ...styles.b}-- destroys C++ state, causes unpredictable style resolution, triggers dev warnings - Using
useUnistylesin regular components -- forces full re-renders, defeats the zero-render architecture - Calling
StyleSheet.createbeforeStyleSheet.configure-- styles won't have access to themes or breakpoints - Importing
StyleSheetfromreact-nativeinstead ofreact-native-unistyles-- styles work but lose all Unistyles features (themes, variants, breakpoints) - Passing non-serializable arguments to dynamic functions (functions, components, Promises) -- arguments are passed to C++ via
folly::dynamic, non-serializable values crash
Medium Priority Issues:
- Setting both
initialThemeandadaptiveThemes: truein configure -- they are mutually exclusive, Unistyles will throw an error - Missing Babel plugin configuration -- without it, dependency detection, ref borrowing, and scoped variants don't work
- Using
useUnistylesat the root level -- subscribes the entire app tree to every theme/runtime change - Defining breakpoints without a
0value -- at least one breakpoint must be0for CSS-like cascading to work
Gotchas & Edge Cases:
- The
bottominset is NOT dynamic for keyboard -- usert.insets.ime(input method editor) for keyboard-responsive padding - Babel plugin does NOT support moving functions outside
StyleSheet.createor reassigningtheme/rtto other variables -- the analysis is scope-bound ScopedThemedoes not work correctly aboveSuspenseboundaries -- place it inside suspended components- Metro Fast Refresh (HMR) does not propagate child changes to parent
ScopedThemecomponents -- requires manual refresh withUnistylesuniProps are lower priority than inline props -- inline props override uniProps, which override global mappings- Boolean variants use string keys
"true"and"false"-- they are distinct from adefaultvariant - All themes must share the same TypeScript type -- mismatched theme shapes cause type errors
- On web, Unistyles converts theme colors to CSS variables -- theme switching swaps a single class on
<body>, no JS recomputation UnistylesRuntimegetters are non-reactive outside StyleSheet -- useuseUnistylesorwithUnistylesfor reactive access in componentsStyleSheet.addChangeListener()(v3.1.0+) is the escape hatch for animation libraries that need runtime update notifications
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md
(You MUST import StyleSheet from react-native-unistyles, NOT from react-native -- the Unistyles version is a superset that enables all features)
(You MUST call StyleSheet.configure() BEFORE any StyleSheet.create() -- configure in your entry file before importing components)
(You MUST use array syntax [styles.a, styles.b] for merging styles -- NEVER spread {...styles.a, ...styles.b} as it destroys C++ state)
(You MUST NOT use useUnistyles hook in regular components -- it forces full re-renders, defeating Unistyles' zero-render architecture)
(You MUST pass only serializable arguments to dynamic functions -- strings, numbers, booleans, arrays, objects (no functions or components))
Failure to follow these rules will cause broken styles, unnecessary re-renders, and runtime crashes from the C++ core.
</critical_reminders>
Signals
- GitHub stars
- 24
- Forks
- 8
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
mobile-styling-unistyles- Source
- github.com/agents-inc/skills