rn-variant
Version:
Light-weight, type-safe style variants for React Native.
134 lines (103 loc) • 3.95 kB
Markdown
# rn-variant
Light-weight, type-safe style variants for React Native.
Inspired by [Class Variance Authority](https://github.com/joe-bell/cva) and [Unistyle](https://github.com/jpudysz/react-native-unistyles), rn-variant lets you declare, combine and re-use complex styling options—with zero runtime dependencies.
⸻
## Features
• Tiny & dependency-free – just TypeScript + StyleSheet.
• Multi-variant syntax – compose size, color, state, etc.
• Compound variants – attach styles to specific variant combos.
• Boolean flags – declare rounded, disabled, etc. as simple booleans.
• Strict typings – variant values are autocompleted and type-checked.
• Hook-based API – const { style } = useVariants({ size: 'md', color: 'primary' }).
⸻
## 📦 Installation
```bash
npm install rn-variant
# or
yarn add rn-variant
```
⸻
## ⚡ Quick start
```typescript
import { createVariantStyles } from "rn-variant";
/* 1. Declare your variants */
const buttonVariants = createVariantStyles({
base: {
borderRadius: 6,
alignItems: "center",
justifyContent: "center",
paddingVertical: 12,
paddingHorizontal: 24,
},
variants: {
size: {
sm: { paddingVertical: 8, paddingHorizontal: 16 },
md: { paddingVertical: 12, paddingHorizontal: 24 },
lg: { paddingVertical: 16, paddingHorizontal: 32 },
},
color: {
primary: { backgroundColor: "#007bff" },
secondary: { backgroundColor: "#6c757d" },
outline: {
backgroundColor: "transparent",
borderWidth: 1,
borderColor: "#007bff",
},
},
rounded: {
// boolean flag
true: { borderRadius: 9999 },
false: {},
},
},
/* optional */
compoundVariants: [
{
variants: { color: "primary", size: "lg" },
style: { shadowOpacity: 0.25, shadowRadius: 4 },
},
],
});
/* 2. Extract prop types (optional) */
import type { InferVariant } from "rn-variant";
export type ButtonVariantProps = InferVariant<typeof buttonVariants>;
/* 3. Use in a component */
export const Button = ({
title,
...variant
}: ButtonVariantProps & { title: string; onPress?: () => void }) => {
const { style } = buttonVariants.useVariants(variant);
return (
<TouchableOpacity style={style} onPress={variant.onPress}>
<Text style={{ color: variant.color === "outline" ? "#007bff" : "#fff" }}>
{title}
</Text>
</TouchableOpacity>
);
};
```
⸻
## API
### createVariantStyles(options)
| option | type | default | description |
| ------------------ | ------------------------------------------------------------------ | ------- | ---------------------------------------------- |
| `base` | `Partial<ViewStyle \| TextStyle \| ImageStyle>` | `{}` | style applied to all variants |
| `variants` | `Record<group, Record<key, Partial<RNStyle>>>` | — | top-level variant groups (e.g. size, color) |
| `compoundVariants` | `Array<{ variants: Partial<variants>; style: Partial<RNStyle>; }>` | `[]` | extra style when all matching keys are present |
Returns:
```typescript
{
styles: { [Group in keyof variants]: { [Key in keyof variants[Group]]: { style: RNStyle } } };
useVariants(input: VariantInput): { style: RNStyle };
}
```
### useVariants(input)
Accepts an object where keys are variant groups and values are:
• a variant key ('lg', 'primary', …), or
• boolean when the group defines { true, false }.
It merges base → individual variants → matching compoundVariants and returns { style } ready for `<View style={...} />`.
### InferVariant<typeof variants>
Utility type that extracts the props shape expected by useVariants, handy for declaring component props.
⸻
## 📄 License
MIT © 2025 Simon Boisset