npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-styles-modifier

v1.0.1

Published

A fluent API builder for creating type-safe React Native styles with method chaining and theme support

Readme

React Native Modifier

A fluent, chainable style builder for React Native — inspired by Material UI's sx prop and Jetpack Compose's Modifier pattern.

Why This Exists

This library was created to solve a practical problem I encountered while working with React Native styling.

While I enjoy using StyleSheet and object-based styles, I often needed a more structured and reusable way to define styles across an application. In many projects, styles become scattered across components, making them harder to reuse, maintain, and evolve as the design system grows.

This library provides a centralized styling approach built around a fluent API that makes styles easier to compose, extend, and standardize.

Key Benefits

With this approach, you can:

  • Define a theme once and use it consistently across your application
  • Create custom design tokens (colors, spacing, typography, etc.)
  • Build reusable style patterns that can be extended or modified easily
  • Maintain a consistent design language through a predictable API
  • Keep styling logic centralized instead of spreading StyleSheet.create() calls across multiple components

Because styles are built using a fluent API, you can compose and adjust them flexibly while keeping the code readable and predictable.

The goal is to make styling simpler to scale, easier to maintain, and more consistent across the entire application.

What You Get

Chainable Style API — Build styles fluently without worrying about object keys
Built-in Theme System — Colors, modes, and palettes ready to use (customizable)
Extensible Modifier Class — Add your own custom style methods once, use everywhere
Design Tokens — Pre-configured spacing, border radius, font sizes, shadows, and more
Zero Configuration — Works out of the box, customize only what you need
Zero Dependencies — Pure TypeScript, no external packages
Type-Safe — Full TypeScript support with autocomplete for all style properties

Installation

npm install react-native-styles-modifier
# or
yarn add react-native-styles-modifier
# or
pnpm add react-native-styles-modifier

Quick Start

Basic Usage (No Theme)

The simplest way to use the library — just import and start chaining styles:

import { View, Text } from "react-native";
import { createModifier } from "react-native-styles-modifier";

// Create a modifier instance
const modifier = createModifier();

function MyComponent() {
  // Build styles with a fluent API
  const containerStyle = modifier()
    .padding(16)
    .backgroundColor("#ffffff")
    .borderRadius(8)
    .flexDirection("row")
    .justifyContent("space-between")
    .alignItems("center")
    .build();

  const textStyle = modifier()
    .fontSize(16)
    .fontWeight("600")
    .color("#333333")
    .build();

  return (
    <View style={containerStyle}>
      <Text style={textStyle}>Hello, Modifier!</Text>
    </View>
  );
}

With Theme Support (Recommended)

Unlock the full power with built-in theming. Initialize once in your app root:

// App.tsx or index.tsx
import { useSyncExternalStore } from "react";
import { useColorScheme } from "react-native";
import {
  initializeTheme,
  initializeColorScheme,
} from "react-native-styles-modifier";

// Step 1: Initialize theme (light or dark)
initializeTheme("light");

// OR
// Using the useColorScheme hook from React Native, to get the current system theme
const colorScheme = useColorScheme();

initializeTheme(colorScheme as ThemeMode);

// OR
// Using the async localStorage to persist the current theme

const currentTheme = await storage.getItem(CURRENT_THEME);
const theme = currentTheme ?? colorScheme;

initializeTheme(theme);

// Step 2: Initialize color scheme with React's useSyncExternalStore
initializeColorScheme(useSyncExternalStore);

Then use the theme anywhere in your app:

import { View, Text, Button } from "react-native";
import { createModifier, useModifierTheme } from "react-native-styles-modifier";

const modifier = createModifier();

function ThemedComponent() {
  // Get colors, mode, and theme controls in one hook
  const { colors, mode, toggleTheme } = useModifierTheme();

  // Option 1: Extract style to a variable
  const containerStyle = modifier()
    .backgroundColor(colors.surface)
    .padding(24)
    .borderRadius(12)
    .build();

  return (
    <View style={containerStyle}>
      <Text style={{ color: colors.onSurface }}>
        Current theme: {mode}
      </Text>
      <Button title="Toggle Theme" onPress={toggleTheme} />

      {/* Option 2: Inline style (great for one-off styles) */}
      <View
        style={modifier()
          .backgroundColor(colors.background)
          .marginHorizontal(8)
          .borderRadius(12)
          .padding(12)
          .build()}
      />
    </View>
  );
}

Debugging Re-renders

Track when styles are being rebuilt during development by passing __DEV__:

// Pass __DEV__ to enable logging in development only
const modifier = createModifier(__DEV__);

// Use build() with a label to track rebuilds
const containerStyle = modifier()
  .backgroundColor(colors.surface)
  .padding(24)
  .borderRadius(12)
  .build("CONTAINER");

// Console output (dev only): "BUILD LOG FOR: CONTAINER"

Important: Always pass __DEV__ (React Native's global) instead of hardcoding true or false. This ensures:

  • Logging works in development
  • Zero logging in production builds

Conditional Styling

Apply styles conditionally with the applyIf method:

const modifier = createModifier();

function TodoItem({ todo }) {
  const { colors } = useModifierTheme();

  // Option 1: Extract to variable
  const itemStyle = modifier()
    .padding(12)
    .borderRadius(8)
    .backgroundColor(colors.surface)
    .applyIf(todo.completed, (m) =>
      m.backgroundColor(colors.inversePrimary)
    )
    .build();

  return <View style={itemStyle}>...</View>;
}

function TodoItemInline({ todo }) {
  const { colors } = useModifierTheme();

  // Option 2: Inline with conditional styling
  return (
    <View
      style={modifier()
        .backgroundColor(colors.background)
        .marginHorizontal(8)
        .borderRadius(12)
        .padding(12)
        .applyIf(todo.completed, (m) =>
          m.backgroundColor(colors.inversePrimary)
        )
        .build()}
    >
      <Text>{todo.title}</Text>
    </View>
  );
}

Extending the Modifier Class

One of the most powerful features is the ability to extend the Modifier class with your own custom style methods. Define them once, use them everywhere.

Creating Custom Style Methods

import { Modifier } from "react-native-styles-modifier";

// Extend the Modifier class with your custom methods
class CustomModifier extends Modifier {
  // Conditional method for platform-specific styling
  android(callback: (modifier: this) => this) {
    if (Platform.OS === "android") {
      return callback(this);
    }
    return this;
  }

  // Custom card style
  card() {
    this.styles.backgroundColor = "#ffffff";
    this.styles.borderRadius = 12;
    this.styles.padding = 16;
    this.styles.shadowColor = "#000";
    this.styles.shadowOffset = { width: 0, height: 2 };
    this.styles.shadowOpacity = 0.1;
    this.styles.shadowRadius = 8;
    this.styles.elevation = 3;
    return this;
  }

  // Custom button style
  primaryButton() {
    this.styles.backgroundColor = "#007AFF";
    this.styles.paddingVertical = 12;
    this.styles.paddingHorizontal = 24;
    this.styles.borderRadius = 8;
    this.styles.alignItems = "center";
    this.styles.justifyContent = "center";
    return this;
  }

  // Custom text preset
  heading() {
    this.styles.fontSize = 24;
    this.styles.fontWeight = "bold";
    this.styles.marginBottom = 8;
    return this;
  }

  // Custom spacing using your design system
  spacingLarge() {
    this.styles.padding = 24;
    return this;
  }
}

// Create your custom factory function
export function createCustomModifier() {
  return () => new CustomModifier();
}

Now use your custom methods anywhere:

import { createCustomModifier } from "./CustomModifier";

const modifier = createCustomModifier();

function MyScreen() {
  const cardStyle = modifier().card().spacingLarge().build();
  const buttonStyle = modifier().primaryButton().build();
  const titleStyle = modifier()
    .android((m) => m.paddingBottom(0).backgroundColor("#fff"))
    .heading()
    .color("#333")
    .build();

  return (
    <View style={cardStyle}>
      <Text style={titleStyle}>Welcome</Text>
      <Pressable style={buttonStyle}>
        <Text>Click Me</Text>
      </Pressable>
    </View>
  );
}

Custom Design Tokens

Extend the Modifier with your design system's tokens:

class DesignSystemModifier extends Modifier {
  // Spacing tokens
  spacingXs() {
    this.styles.padding = 4;
    return this;
  }
  spacingSm() {
    this.styles.padding = 8;
    return this;
  }
  spacingMd() {
    this.styles.padding = 16;
    return this;
  }
  spacingLg() {
    this.styles.padding = 24;
    return this;
  }
  spacingXl() {
    this.styles.padding = 32;
    return this;
  }

  // Border radius tokens
  roundedSm() {
    this.styles.borderRadius = 4;
    return this;
  }
  roundedMd() {
    this.styles.borderRadius = 8;
    return this;
  }
  roundedLg() {
    this.styles.borderRadius = 12;
    return this;
  }
  roundedFull() {
    this.styles.borderRadius = 9999;
    return this;
  }

  // Font size tokens
  textXs() {
    this.styles.fontSize = 12;
    return this;
  }
  textSm() {
    this.styles.fontSize = 14;
    return this;
  }
  textBase() {
    this.styles.fontSize = 16;
    return this;
  }
  textLg() {
    this.styles.fontSize = 18;
    return this;
  }
  textXl() {
    this.styles.fontSize = 20;
    return this;
  }
  text2xl() {
    this.styles.fontSize = 24;
    return this;
  }

  // Shadow tokens
  shadowSm() {
    this.styles.shadowColor = "#000";
    this.styles.shadowOffset = { width: 0, height: 1 };
    this.styles.shadowOpacity = 0.05;
    this.styles.shadowRadius = 2;
    this.styles.elevation = 1;
    return this;
  }

  shadowMd() {
    this.styles.shadowColor = "#000";
    this.styles.shadowOffset = { width: 0, height: 2 };
    this.styles.shadowOpacity = 0.1;
    this.styles.shadowRadius = 4;
    this.styles.elevation = 3;
    return this;
  }
}

export function createModifier() {
  return new DesignSystemModifier();
}

Use your tokens:

const modifier = createModifier();

const style = modifier().spacingMd().roundedLg().textBase().shadowMd().build();

Theme System

Available Colors

24 semantic colors following Material Design 3 guidelines:

| Category | Colors | | -------------- | ------------------------------------------------------------------------ | | Primary | primary, onPrimary, primaryContainer, onPrimaryContainer | | Secondary | secondary, onSecondary, secondaryContainer, onSecondaryContainer | | Tertiary | tertiary, onTertiary, tertiaryContainer, onTertiaryContainer | | Error | error, onError, errorContainer, onErrorContainer | | Background | background, onBackground | | Surface | surface, onSurface, surfaceVariant, onSurfaceVariant | | Outline | outline, outlineVariant | | Inverse | inverseSurface, inverseOnSurface, inversePrimary | | Other | shadow, scrim |

Theme Controls

const { colors, mode, toggleTheme, setTheme } = useModifierTheme();
  • colors — Current color palette (light or dark)
  • mode — Current theme mode ("light" | "dark")
  • toggleTheme() — Toggle between light/dark
  • setTheme(mode) — Set specific theme mode
  • await storage.setItem(mode) — To save the theme in the async storage

Custom Colors

Add your own brand colors:

// Declare custom colors
declare module "react-native-styles-modifier" {
  interface CustomColorScheme {
    brandPrimary: string;
    success: string;
  }
}

// Initialize with custom colors (Material Design 3 colors included automatically)
const light = { brandPrimary: "#FF6B6B", success: "#51CF66" };
const dark = { brandPrimary: "#FF8787", success: "#69DB7C" };
initializeTheme("light", light, dark);

// Use anywhere
const { colors } = useModifierTheme();
modifier.backgroundColor(colors.brandPrimary).build();

Complete API Reference

The Modifier class provides 121+ style methods covering all React Native StyleSheet properties.

Layout & Flexbox (20 methods)

// Flex container
.flex(1)
.flexDirection("row" | "column" | "row-reverse" | "column-reverse")
.flexWrap("wrap" | "nowrap" | "wrap-reverse")
.flexGrow(1)
.flexShrink(1)
.flexBasis(100)

// Alignment
.alignItems("center" | "flex-start" | "flex-end" | "stretch" | "baseline")
.alignSelf("auto" | "center" | "flex-start" | "flex-end" | "stretch" | "baseline")
.alignContent("center" | "flex-start" | "flex-end" | "stretch" | "space-between" | "space-around" | "space-evenly")
.justifyContent("center" | "flex-start" | "flex-end" | "space-between" | "space-around" | "space-evenly")

// Spacing
.gap(8)
.rowGap(8)
.columnGap(8)

// Position
.position("absolute" | "relative" | "static")
.display("flex" | "none" | "contents")
.overflow("visible" | "hidden" | "scroll")
.direction("ltr" | "rtl" | "inherit")
.boxSizing("border-box" | "content-box")
.zIndex(10)

Spacing - Padding (14 methods)

.padding(16)
.paddingVertical(16)
.paddingHorizontal(16)
.paddingTop(16)
.paddingBottom(16)
.paddingLeft(16)
.paddingRight(16)
.paddingStart(16)
.paddingEnd(16)
.paddingBlock(16)
.paddingBlockStart(16)
.paddingBlockEnd(16)
.paddingInline(16)
.paddingInlineStart(16)
.paddingInlineEnd(16)

Spacing - Margin (14 methods)

.margin(16)
.marginVertical(16)
.marginHorizontal(16)
.marginTop(16)
.marginBottom(16)
.marginLeft(16)
.marginRight(16)
.marginStart(16)
.marginEnd(16)
.marginBlock(16)
.marginBlockStart(16)
.marginBlockEnd(16)
.marginInline(16)
.marginInlineStart(16)
.marginInlineEnd(16)

Dimensions (6 methods)

.width(100)
.height(100)
.minWidth(50)
.minHeight(50)
.maxWidth(200)
.maxHeight(200)

Positioning (13 methods)

.top(0)
.bottom(0)
.left(0)
.right(0)
.start(0)
.end(0)
.inset(0)
.insetBlock(0)
.insetBlockStart(0)
.insetBlockEnd(0)
.insetInline(0)
.insetInlineStart(0)
.insetInlineEnd(0)

Borders (25 methods)

// Border width
.borderWidth(1)
.borderTopWidth(1)
.borderBottomWidth(1)
.borderLeftWidth(1)
.borderRightWidth(1)
.borderStartWidth(1)
.borderEndWidth(1)

// Border color
.borderColor("#000")
.borderTopColor("#000")
.borderBottomColor("#000")
.borderLeftColor("#000")
.borderRightColor("#000")
.borderStartColor("#000")
.borderEndColor("#000")
.borderBlockColor("#000")
.borderBlockStartColor("#000")
.borderBlockEndColor("#000")

// Border radius
.borderRadius(8)
.borderTopLeftRadius(8)
.borderTopRightRadius(8)
.borderBottomLeftRadius(8)
.borderBottomRightRadius(8)
.borderTopStartRadius(8)
.borderTopEndRadius(8)
.borderBottomStartRadius(8)
.borderBottomEndRadius(8)
.borderStartStartRadius(8)
.borderStartEndRadius(8)
.borderEndStartRadius(8)
.borderEndEndRadius(8)

// Border style
.borderStyle("solid" | "dotted" | "dashed")
.borderCurve("circular" | "continuous")

Colors & Visual (5 methods)

.backgroundColor("#fff")
.opacity(0.5)
.elevation(3)  // Android shadow
.backfaceVisibility("visible" | "hidden")
.cursor("auto" | "pointer")

Shadows (4 methods)

.shadowColor("#000")
.shadowOffset({ width: 0, height: 2 })
.shadowOpacity(0.25)
.shadowRadius(3.84)

Transforms (2 methods)

.transform([{ rotate: "45deg" }, { scale: 1.2 }])
.transformOrigin(["50%", "50%"])

Outline (4 methods)

.outlineColor("#000")
.outlineOffset(2)
.outlineStyle("solid" | "dotted" | "dashed")
.outlineWidth(1)

Text Styles (17 methods)

.color("#000")
.fontFamily("Arial")
.fontSize(16)
.fontStyle("normal" | "italic")
.fontWeight("normal" | "bold" | "100" | "200" | ... | "900")
.fontVariant(["small-caps"])
.letterSpacing(1)
.lineHeight(24)
.textAlign("auto" | "left" | "right" | "center" | "justify")
.textAlignVertical("auto" | "top" | "bottom" | "center")
.textDecorationColor("#000")
.textDecorationLine("none" | "underline" | "line-through" | "underline line-through")
.textDecorationStyle("solid" | "double" | "dotted" | "dashed")
.textShadowColor("#000")
.textShadowOffset({ width: 0, height: 1 })
.textShadowRadius(2)
.textTransform("none" | "capitalize" | "uppercase" | "lowercase")
.includeFontPadding(false)
.userSelect("auto" | "none" | "text" | "contain" | "all")
.verticalAlign("auto" | "top" | "bottom" | "middle")
.writingDirection("auto" | "ltr" | "rtl")

Utilities (2 methods)

// Conditional styling
.applyIf(condition, (m) => m.backgroundColor("red"))

// Build final style object
.build()

Real-World Examples

Button with Variants

function CustomButton({ title, variant = "primary", onPress }) {
  const { colors } = useModifierTheme();

  const buttonStyle = createModifier()
    .paddingVertical(12)
    .paddingHorizontal(24)
    .borderRadius(8)
    .alignItems("center")
    .applyIf(variant === "primary", (m) => m.backgroundColor(colors.primary))
    .applyIf(variant === "outline", (m) =>
      m.backgroundColor("transparent").borderWidth(1).borderColor(colors.outline)
    )
    .build();

  return (
    <Pressable style={buttonStyle} onPress={onPress}>
      <Text>{title}</Text>
    </Pressable>
  );
}

Advanced Usage

Responsive Styles

const { width } = Dimensions.get("window");
const isSmall = width < 375;

const style = createModifier()
  .padding(16)
  .applyIf(isSmall, (m) => m.padding(12).fontSize(14))
  .applyIf(!isSmall, (m) => m.padding(20).fontSize(16))
  .build();

TypeScript Support

Full TypeScript support with exported types:

import type {
  Modifier,
  ViewStyle,
  TextStyle,
  ModifierStyle,
  StyleSheetLike,
  ColorScheme,
  ThemeMode,
  ThemeManager,
  ExternalStateManager,
} from "react-native-styles-modifier";

| Type | Description | | ------------------------- | ---------------------------------------------------- | | Modifier | Main modifier class | | ViewStyle | View style properties (all React Native view styles) | | TextStyle | Text style properties (extends ViewStyle) | | ModifierStyle | Combined style type (ViewStyle & TextStyle) | | StyleSheetLike | Interface for StyleSheet-compatible objects | | ColorScheme | Theme color scheme interface (24 semantic colors) | | ThemeMode | "light" \| "dark" | | ThemeManager | Theme manager class | | ExternalStateManager<T> | External state adapter interface |

FAQ

Q: Do I need to install any dependencies?
A: No! This library has zero dependencies. You only need React Native.

Q: Does this affect performance?
A: No. The modifier compiles to standard React Native styles. Theme changes only re-render components that use useModifierTheme().

Q: How do I toggle between light and dark mode?
A: Use the toggleTheme() function from useModifierTheme() hook, or call getThemeManager().toggleTheme().

Q: Do I need to initialize the theme system?
A: Only if you want to use theme features. For basic styling without themes, just use createModifier() directly.

Q: What is useSyncExternalStore and why do I need it?
A: It's React's built-in hook (React 18+, React Native 0.70+) that enables reactive theme updates. You pass it once during initialization with initializeColorScheme(useSyncExternalStore).

Q: Can I extend the Modifier class with my own methods?
A: Yes! Extend the Modifier class and create your own factory function. See the "Extending the Modifier Class" section.

Q: Is this compatible with Expo?
A: Yes! Works with Expo, bare React Native, and any React Native setup.

Q: Can I customize the color palette?
A: Yes! Pass your custom colors to initializeTheme("light", customLightColors, customDarkColors). Material Design 3 colors are included by default.

Q: How do I create reusable components?
A: Extend the Modifier class with custom methods (like .card(), .button()) and use them throughout your app.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT


Made for React Native developers who love clean, reusable styles