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.
Overview
Section titled “Overview”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
Key Features
Section titled “Key Features”- Simple and Unified API: A single
useStyled
hook to define both styling and the component. - Declarative Configuration: Define
base
,variants
,defaultVariants
, andcompoundVariants
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
orclassName
) and React Native (viastyle
orclassName
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) andclassName
(strings) from all sources (base, variants, compounds, direct props), usingtailwind-merge
to resolve utility class conflicts whenclassName
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
).
Why Was use-styled
Created?
Section titled “Why Was use-styled Created?”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:
- Define the variant logic separately.
- Extract the variant types.
- Define the component’s props interface, including the variant types.
- 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 separatelyexport 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 typestype ButtonVariants = VariantProps<typeof buttonLogic>;
// 3. Define component props, including variantsinterface 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 logicexport 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.
When and Why to Use use-styled
Section titled “When and Why to Use use-styled”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.