react-native-styling-kit
v1.1.1
Published
Toolkit for theming and variants in React Native
Readme
React Native Style Kit
A Styling API for React Native, designed as a lightweight set of tools to quickly build beautiful apps. You get:
- 🎨 Theming
- 📏 Breakpoint logic
- 🧩 Variants/compound variants inspired by CVA
- 📱 Access to runtime values (safe area insets, screen dimensions) in stylesheets
- 🔩 Presets for building powerful utility stylesheets, for a "tailwind-like" experience
All with no babel/metro plugins, full compatibility with 3rd party components, and a focus on performance.
Installation
You know the drill 😄
pnpm install react-native-styling-kitYou're almost ready to go! You'll also need to wrap your app in <StyleKitProvider> (more on this later)
import { StyleKitProvider } from 'react-native-styling-kit';
const Main = () => {
return (
<StyleKitProvider>
<App />
</StyleKitProvider>
);
}Creating styles
Styles are created via makeUseStyles() which is a drop-in replacement for StyleSheet.create(). This function returns a hook you can call within your component to access the styles.
import { makeUseStyles } from 'react-native-styling-kit';
const useStyles = makeUseStyles({
root: {
backgroundColor: 'white',
padding: 16,
}
});
const Button = () => {
const styles = useStyles();
return <Pressable style={styles.root}/>
}🎨 Theming
Define a theme, then pass it to your StyleKitProvider. If you're using TypeScript, also augment the theme type to get the correct typings across your app.
const theme = {...};
type ThemeType = typeof theme;
declare module 'react-native-styling-kit' {
interface StyleKitTheme extends ThemeType {}
}
const Main = () => {
return (
<StyleKitProvider theme={theme}>
<App />
</StyleKitProvider>
);
}You can then create styles that access the theme by passing a function tomakeUseStyles() instead of an object
import { makeUseStyles } from 'react-native-styling-kit';
const useStyles = makeUseStyles(({ theme }) => ({
root: {
backgroundColor: theme.colors.background,
padding: theme.spacing.md,
}
}));
const Button = () => {
const styles = useStyles();
return <Pressable style={styles.root}/>
}You can also access the theme directly with the useTheme() hook.
import { useTheme } from 'react-native-styling-kit';
const Button = () => {
const theme = useTheme();
return <Pressable
style={(pressed) => ({
backgroundColor: pressed ? theme.colors.highlight : theme.colors.background
})}
/>
}
🧩 Variants
To use variants, first define a type for your variants, and pass it as a generic to makeUseStyles(). You can then define variant-specific styles within the variants key of your style definition.
Then, in the useStyles() hook within your component, pass it an object with the current variant values.
import { makeUseStyles } from 'react-native-styling-kit';
interface ButtonVariants {
variant: 'outlined' | 'filled';
}
// Note the double parentheses here "()({...})" required for TypeScript to infer the types correctly
const useStyles = makeUseStyles<ButtonVariants>()({
root: {
variants: {
outlined: { ... },
filled: { ... },
}
}
});
const Button = ({ variant = 'filled' }: Partial<ButtonVariants>) => {
const styles = useStyles({ variant });
return <Pressable style={styles.root}/>
}Compound variants
You can also define compound variants that apply when multiple variant conditions are met
import { makeUseStyles } from 'react-native-styling-kit';
interface ButtonVariants {
variant: 'outlined' | 'filled';
size: 'sm' | 'md' | 'lg';
}
const useStyles = makeUseStyles<ButtonVariants>()(() => ({
root: {
variants: {...},
compoundVariants: [
{
size: 'sm',
variant: 'outlined',
style: { ... }
}
]
}
}));Note: Compound variants are applied in the order they are defined, so later definitions will override earlier ones. Compound variants also take precedence over regular variants.
📱 Runtime values
You can also access runtime values such as screen dimensions or safe area insets within your stylesheets, through the rt value passed to the style function
import { makeUseStyles } from 'react-native-styling-kit';
const useStyles = makeUseStyles(({rt}) => ({
root: {
paddingTop: rt.insets.top,
}
}));
const Button = () => {
const styles = useStyles();
return <Pressable style={styles.root}/>
}📏 Breakpoints
You'll first need to configure a set of breakpoints, and pass them to your StyleKitProvider
const breakpoints = {
xs: 0, // First breakpoint should start with 0
sm: 360,
md: 768,
lg: 1024,
};
type BreakpointType = typeof breakpoints;
declare module 'react-native-styling-kit' {
interface StyleKitBreakpoints extends BreakpointType {}
}
const Main = () => {
return (
<StyleKitProvider breakpoints={breakpoints}>
<App />
</StyleKitProvider>
);
}You can create breakpoint conditional styles by using the bp key within your style definitions.
import { makeUseStyles } from 'react-native-styling-kit';
const useStyles = makeUseStyles(({ bp }) => ({
root: {
height: 32,
// Apply a different style above the 'md' breakpoint
...bp.above.md({
height: 48,
})
}
}));🔩 Utility Stylesheets
Having a set of utility styles for applying common styles such as margins, paddings, and flex properties is an especially powerful tool for composing UI. To help with this, react-native-styling-kit provides a number of presets that can be used to compose utility stylesheets.
import { preset, dynamicSpacing } from 'react-native-styling-kit/utility';
// Create a stylesheet by composing different utility presets
const a = StyleSheet.create({
// Preset includes a predefined set of utility styles covering flexbox, positioning, opacity, and more
...preset,
// You can also generate spacing tokens
...dynamicSpacing({
s: 12,
m: 16,
l: 24,
} as const)
});
// Usage
<View style={[a.flex_row, a.px_m]}>
<Text style={[a.text_center, a.opacity_50]}>Hello, world!</Text>
</View>Performance
react-native-styling-kit is designed to be as performant as possible without leveraging any compile-time optimisations. Only styles that depend on theme or runtime values subscribe to state updates (although in practice these are not likely to change often).
Styles are memoized and cached to ensure they are only recalculated when absolutely necessary. Computed styles are also passed to StyleSheet.create() to take advantage of the optimisations provided by React Native.
