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.
Signature
Section titled “Signature”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 basecomponent
.
component
Argument
Section titled “component Argument”The first argument can be:
- HTML Element String: Ex:
'div'
,'button'
,'span'
. (For React Web) - React Native Component: Ex:
View
,Text
,Pressable
fromreact-native
. - Functional or Class React/React Native Component: Any custom component that accepts
className
and/orstyle
, along with other props.useStyled
will automatically infer the props accepted by this component.
config
Argument
Section titled “config Argument”This object defines all the styling and variant logic.
base
(Optional)
Section titled “base (Optional)”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' }}
variants
(Optional)
Section titled “variants (Optional)”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} } }}
defaultVariants
(Optional)
Section titled “defaultVariants (Optional)”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, }}
compoundVariants
(Optional)
Section titled “compoundVariants (Optional)”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 directlyclassName
/style
) within the object defines the props to be applied when all conditions are met. - Props from
compoundVariants
are merged after props frombase
and activevariants
.
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 } } } ]}
Prop Forwarding
Section titled “Prop Forwarding”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
.
Merging className
and style
Section titled “Merging className and style”useStyled
intelligently merges className
and style
props from all sources (base
, variants
, compoundVariants
, and props passed directly to the component):
className
: Strings are combined usingclsx
. If you are using Tailwind/NativeWind, conflicts between utility classes are automatically resolved usingtailwind-merge
.style
: Style objects are merged (shallow merge).- Precedence: Props passed directly to the component have the highest precedence, followed by
compoundVariants
,variants
, and finallybase
. - Caution: As discussed in Advanced Usage > Overriding Styles, avoid defining the same property in both
style
andclassName
for better performance and clarity.
Complete Example
Section titled “Complete Example”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> );}
TypeScript
Section titled “TypeScript”useStyled
has strong TypeScript integration. It infers allowed props from the base Component
and validates the config
. See more on the TypeScript Integration page.