Skip to content

useStyled Hook

The useStyled hook is the main API of the use-styled library. It allows you to create styled React and React Native components, encapsulating styling logic, variants, and default props declaratively.

function useStyled<T extends React.ElementType, C extends Config<T>>(
component: T,
config: C
): StyledComponent<T, C>;
  • component: The base component you want to style.
  • config: A configuration object that defines styles, variants, and behaviors.
  • Returns: A new React component (StyledComponent) that applies the configuration to the base component.

The first argument can be:

  • HTML Element String: Ex: 'div', 'button', 'span'. (For React Web)
  • React Native Component: Ex: View, Text, Pressable from react-native.
  • Functional or Class React/React Native Component: Any custom component that accepts className and/or style, along with other props. useStyled will automatically infer the props accepted by this component.

This object defines all the styling and variant logic.

An object containing props that will always be applied to the base Component.

  • Use className to apply CSS classes (Tailwind, NativeWind, etc.).
  • Use style to apply inline styles (React/React Native style objects).
  • Pass any other valid props for the base Component (e.g., type, data-testid, accessibilityRole, etc.).
config: {
base: {
className: 'font-sans transition-colors',
style: { margin: 0 },
type: 'button', // If Component is 'button'
}
}

Defines the different visual or behavioral states of the component.

  • The main key is the variant name (which will become a prop on the final component).
  • The inner keys are the possible values for that variant (strings or booleans).
  • The final value is an object of props (className, style, or others) to be applied when that specific variant is active.
config: {
variants: {
intent: { // Variant name: 'intent'
primary: { className: 'bg-blue-500 text-white' }, // Value: 'primary'
secondary: { className: 'bg-gray-200 text-black' }, // Value: 'secondary'
},
size: { // Variant name: 'size'
small: { className: 'px-2 py-1 text-sm' },
medium: { className: 'px-4 py-2 text-base' },
},
disabled: { // Variant name: 'disabled'
true: { className: 'opacity-50 cursor-not-allowed' }, // Boolean value: true
// false: { ... } // Optional: styles for when disabled={false}
}
}
}

Specifies the default values for the defined variants. If a variant prop is not passed to the styled component, the value defined here will be used.

config: {
variants: { /* ... */ },
defaultVariants: {
intent: 'primary',
size: 'medium',
disabled: false,
}
}

Allows applying additional props only when a specific combination of variants is active. It’s an array of objects.

  • Each object in the array defines the conditions (pairs variantName: variantValue).
  • The props property (or directly className/style) within the object defines the props to be applied when all conditions are met.
  • Props from compoundVariants are merged after props from base and active variants.
config: {
variants: { /* ... */ },
compoundVariants: [
{
intent: 'primary',
disabled: true,
props: {
className: 'bg-blue-800 border-blue-900',
style: { /* can add styles too */ }
}
},
{
intent: 'secondary',
size: 'small',
props: {
style: { borderWidth: 1 }
}
}
]
}

Any prop passed to the component returned by useStyled that is not a variant name defined in the config will be automatically forwarded to the base Component.

This includes ref, event handlers (onClick, onLayout, etc.), data-* attributes, aria-*, id, and any other props specific to the base Component.

useStyled intelligently merges className and style props from all sources (base, variants, compoundVariants, and props passed directly to the component):

  • className: Strings are combined using clsx. If you are using Tailwind/NativeWind, conflicts between utility classes are automatically resolved using tailwind-merge.
  • style: Style objects are merged (shallow merge).
  • Precedence: Props passed directly to the component have the highest precedence, followed by compoundVariants, variants, and finally base.
  • Caution: As discussed in Advanced Usage > Overriding Styles, avoid defining the same property in both style and className for better performance and clarity.
import React from 'react';
import { useStyled } from 'use-styled';
import { Pressable, Text } from 'react-native'; // RN Example
const StyledButton = useStyled(Pressable, {
base: {
className: 'flex-row items-center justify-center rounded-lg shadow',
accessibilityRole: 'button',
style: { borderWidth: 1, borderColor: 'transparent' }, // Base style
},
variants: {
intent: {
primary: { className: 'bg-indigo-600' },
secondary: { className: 'bg-gray-200 border-gray-300' },
danger: { className: 'bg-red-600' },
},
size: {
sm: { className: 'px-3 py-1.5 text-sm' },
md: { className: 'px-4 py-2 text-base' },
lg: { className: 'px-6 py-3 text-lg' },
},
outline: {
true: { className: 'bg-transparent' },
},
disabled: {
true: { className: 'opacity-60' },
},
},
defaultVariants: {
intent: 'primary',
size: 'md',
outline: false,
disabled: false,
},
compoundVariants: [
// Text and border styles for outline variants
{
intent: 'primary',
outline: true,
props: { className: 'border-indigo-600 text-indigo-600' }
},
{
intent: 'secondary',
outline: true,
props: { className: 'border-gray-400 text-gray-700' }
},
{
intent: 'danger',
outline: true,
props: { className: 'border-red-600 text-red-600' }
},
// Adjustment for disabled + primary
{
intent: 'primary',
disabled: true,
props: { className: 'bg-indigo-400' }
},
],
});
// -- Usage --
function App() {
return (
<StyledButton
intent="danger"
size="lg"
outline
onPress={() => console.log('Danger outline lg')}
// className="mt-4" // Can also pass className directly
>
{/* Ideally, use Button.Text from useSlot here, but for simplicity: */}
<Text className="text-red-600 text-lg font-semibold">Delete Action</Text>
</StyledButton>
);
}

useStyled has strong TypeScript integration. It infers allowed props from the base Component and validates the config. See more on the TypeScript Integration page.