Skip to content

Introduction

use-styled is a powerful and elegant library designed to simplify the creation of styled React and React Native components, with robust support for variants. It allows decoupling styling and variant logic from the main component logic, resulting in cleaner, reusable, and easier-to-maintain code.

The library offers a declarative approach to styling components, inspired by solutions like styled-components or Stitches, but focused on an integrated and flexible API. The useStyled hook is the heart of the library, allowing you to create components with:

  • Base styles
  • Configurable variants
  • Compound variants (for specific combinations)
  • Default values for variants
  • Simple and Unified API: A single useStyled hook to define both styling and the component.
  • Declarative Configuration: Define base, variants, defaultVariants, and compoundVariants in a clear and cohesive configuration object.
  • Type-Safe by Design: Fully written in TypeScript, with automatic prop inference and configuration validation to prevent compile-time errors.
  • Platform Agnostic: Works seamlessly with React for Web (via style or className) and React Native (via style or className with NativeWind v4).
  • Tailwind/NativeWind Integration: Use className natively in the configuration to apply utility classes.
  • Multiple Concurrent Variants: Apply and combine multiple variants simultaneously (e.g., size, color, state).
  • Intelligent Merging: Automatically combines style (objects) and className (strings) from all sources (base, variants, compounds, direct props), using tailwind-merge to resolve utility class conflicts when className is used.
  • Automatic Prop Forwarding: Props passed to the styled component that are not variant names are automatically forwarded to the base component (including ref).

While libraries like class-variance-authority (cva) and tailwind-variants are powerful tools for managing style variants, we saw an opportunity to refine the developer experience (DX) by integrating this logic directly into the creation of the React/React Native component.

With approaches like tailwind-variants, despite the excellent variant logic, a multi-step process is often required to connect this logic to a functional React component, especially when using TypeScript:

  1. Define the variant logic separately.
  2. Extract the variant types.
  3. Define the component’s props interface, including the variant types.
  4. Create the React component, manually applying the variant logic and managing the passing/merging of props like className.
import { tv, type VariantProps } from 'tailwind-variants';
// 1. Define variant logic separately
export const buttonLogic = tv({
base: 'px-4 py-1.5 rounded-full hover:opacity-80',
variants: {
color: {
primary: 'bg-blue-500 text-white',
neutral: 'bg-zinc-500 text-black dark:text-white'
},
flat: {
true: 'bg-transparent'
}
},
defaultVariants: {
color: 'primary'
},
compoundVariants: [
{
color: 'primary',
flat: true,
class: 'bg-blue-500/40'
},
{
color: 'neutral',
flat: true,
class: 'bg-zinc-500/20'
}
]
});
// 2. Extract variant types
type ButtonVariants = VariantProps<typeof buttonLogic>;
// 3. Define component props, including variants
interface ButtonProps extends ButtonVariants {
children: React.ReactNode;
// other button-specific props, if any...
className?: string; // Need to manually allow extra className
}
// 4. Create the React component, manually applying the logic
export const Button = (props: ButtonProps) => {
const { children, className, ...variantProps } = props;
return (
<button
// Apply logic and merge with external className
className={buttonLogic({ ...variantProps, className })}
>
{children}
</button>
);
};
// Usage
<Button color="neutral" flat>Click Me</Button>

This process, while functional, can introduce boilerplate and a disconnect between the definition of styles/variants and the component itself.

The Tamagui Inspiration and the Goal of use-styled

Observing the Tamagui API, where the configuration of styles, variants, and props seems more integrated into the component definition, was fundamental. The useStyled(Component, config) API was designed to emulate this sense of cohesion.

The goal of use-styled is therefore to combine the powerful variant logic established by cva and tailwind-variants with a more direct and integrated component creation experience, minimizing boilerplate and bringing the style definition closer to the component declaration, inspired by the fluidity found in Tamagui.

use-styled particularly shines in these scenarios:

  • Creating Design Systems: Ideal for building a consistent and reusable set of UI components.
  • Components with Multiple Visual States: Simplifies components with various variants (size, color, state, etc.) and their combinations.
  • Cross-Platform Development (React + React Native): Offers a unified approach to styling with variants on both platforms.
  • Projects with Tailwind CSS / NativeWind: Integrates seamlessly, leveraging utility classes within a variant system.
  • Refactoring Conditional Styling Logic: Helps organize and simplify complex components with many if/else or ternaries for applying styles.
  • Preference for Integrated DX: If you value an API where the definition of styles and variants is closely tied to the component creation.