Package Exports
- use-styled
- use-styled/dist/index.js
- use-styled/dist/index.mjs
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (use-styled) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
use-styled
A lightweight library for creating styled components, optimized for use with Tailwind CSS and compatible with Server Components.
Key Features
- 🔄 Automatic prop propagation between components
- 📦 Zero dependency on Context API - works in Server Components!
- 🎯 Intelligent typing with value inference for autocompletion
- 🧩 Component composition via slots
- 🔍 Enhanced IntelliSense with valid value suggestions
- 🛡️ Native TypeScript with perfect inference
Installation
npm install use-styled
# or
yarn add use-styled
# or
pnpm add use-styled
Usage Example
import { useStyled, useStyledProps, useStyledSlots } from 'use-styled'
// Simplified syntax to define props with typing
export const ButtonProps = useStyledProps({
// Array of strings = union type
variant: ['primary', 'secondary', 'danger'] as const,
// Array of strings = union type
size: ['sm', 'md', 'lg'] as const,
// Boolean constructor = boolean type
disabled: Boolean,
})
// Create the main component using useStyled
export const ButtonFrame = useStyled('button', {
name: 'ButtonFrame',
props: ButtonProps, // The type system recognizes the types defined above
variants: {
// ✓ Autocomplete will suggest ButtonProps keys: variant, size, disabled
variant: {
// ✓ Autocomplete will suggest valid values: primary, secondary, danger
primary: 'bg-blue-500 hover:bg-blue-600',
secondary: 'bg-gray-200 hover:bg-gray-300',
danger: 'bg-red-500 hover:bg-red-600',
},
size: {
// ✓ Autocomplete will suggest valid values: sm, md, lg
sm: 'h-10',
md: 'h-12',
lg: 'h-14',
},
disabled: {
// ✓ Autocomplete will suggest true and false for boolean values
true: 'opacity-50 cursor-not-allowed',
false: 'opacity-100',
}
},
})
// Create child components that will use the same props
export const ButtonText = useStyled('p', {
name: 'ButtonText',
props: ButtonProps, // Same props object for inference and propagation
variants: {
// ✓ Same type inference here too
variant: {
primary: 'text-white',
secondary: 'text-gray-800',
danger: 'text-white',
},
size: {
sm: 'text-sm',
md: 'text-base',
lg: 'text-lg',
},
disabled: {
true: 'text-opacity-70',
false: 'text-opacity-100',
}
},
})
// Use useStyledSlots to create the final component with its slots
export const Button = useStyledSlots(ButtonFrame, {
slots: {
Text: ButtonText,
},
props: ButtonProps,
})
// Final usage
function App() {
return (
<Button variant="primary" size="sm" disabled={false}>
<Button.Text>Click me</Button.Text>
</Button>
)
}
API
useStyledProps
Creates variant props for a component with simplified typing:
// New type syntax - much cleaner!
const ComponentProps = useStyledProps({
// Array = union type
variant: ['default', 'primary', 'secondary'] as const,
size: ['sm', 'md', 'lg'] as const,
// Constructors = primitive types
disabled: Boolean,
count: Number,
label: String,
// Literal value = literal type
shape: 'rounded',
})
This is equivalent to writing the following TypeScript type:
type ComponentProps = {
variant: 'default' | 'primary' | 'secondary';
size: 'sm' | 'md' | 'lg';
disabled: boolean;
count: number;
label: string;
shape: 'rounded';
}
useStyled
Creates a styled component with variants. The variants are typed based on the props
object provided:
const StyledComponent = useStyled('div', {
name: 'MyComponent', // Optional
props: ComponentProps, // Props created with useStyledProps
base: 'flex items-center justify-center',
variants: {
// The editor will show valid options based on ComponentProps
variant: {
// Autocomplete will only show 'default', 'primary', 'secondary'
default: 'bg-white text-black',
primary: 'bg-blue-500 text-white',
secondary: 'bg-gray-200 text-gray-800',
},
// Other variants are suggested based on ComponentProps
},
defaultVariants: {
variant: 'default',
size: 'md',
disabled: false,
},
styleOnly: ['variant', 'size'], // Props used only for styling and not passed to the DOM
})
styleOnly
Property
The styleOnly
property allows you to specify which variant props are used only to apply styles and should not be passed to the underlying DOM element:
styleOnly: ['variant', 'size', 'shape'] // These props won't appear as HTML attributes
Benefits:
- Avoids React warnings about unknown props
- Keeps the DOM clean (without custom attributes)
- Improves accessibility and SEO
For example, with styleOnly: ['variant']
, the generated element will be:
<!-- With styleOnly -->
<button class="text-white bg-blue-500">Button</button>
<!-- Without styleOnly -->
<button variant="primary" class="text-white bg-blue-500">Button</button>
useStyledSlots
Creates a composite component with slots:
const FinalComponent = useStyledSlots(MainComponent, {
slots: {
// Components that will be accessible as FinalComponent.Slot
Label: LabelComponent,
Icon: IconComponent,
},
props: ComponentProps, // Optional, only for typing
})
Intelligent Typing
The use-styled
library offers a perfect developer experience with automatic type inference:
Clear type definition: Use arrays for union types and constructors for primitive types
const Props = useStyledProps({ variant: ['primary', 'secondary'], // union type disabled: Boolean, // boolean })
IntelliSense in variants: The editor suggests only valid keys and their possible values
variants: { // The editor only allows 'variant' and 'disabled' here variant: { // The editor only allows 'primary' and 'secondary' here } }
Automatic typing in usage: The editor shows valid values when using the component
<Button variant="primary" // Only accepts 'primary' or 'secondary' disabled={false} // Only accepts true or false />
Prop Propagation Between Components
One of the main features of use-styled
is the ability to automatically propagate props from the parent component to its child slots, without having to define them again.
How it works:
- Define shared props with
useStyledProps
:
const ButtonProps = useStyledProps({
variant: ['primary', 'secondary'],
size: ['sm', 'md', 'lg'],
disabled: Boolean,
})
- Use the same props in each component:
const ButtonFrame = useStyled('button', {
props: ButtonProps, // ⚠️ This line is crucial!
variants: {
variant: { /* ... */ },
size: { /* ... */ },
disabled: { /* ... */ },
},
})
const ButtonText = useStyled('p', {
props: ButtonProps, // ⚠️ This line is crucial!
variants: {
variant: { /* ... */ },
size: { /* ... */ },
disabled: { /* ... */ },
},
})
- Create the final component with
useStyledSlots
:
const Button = useStyledSlots(ButtonFrame, {
slots: { Text: ButtonText },
props: ButtonProps, // Optional, only for typing
})
- Use the component - props will be propagated automatically:
<Button variant="primary" size="lg" disabled={true}>
<Button.Text>
{/* variant="primary", size="lg", and disabled={true} are automatically applied here */}
Text styled with the same props as Button
</Button.Text>
</Button>
Important:
For variant propagation to work correctly:
- Create a props object with
useStyledProps
- Pass exactly the same object to the
props
parameter in each component that should share the variants - Use
useStyledSlots
to create the final component
Fine-grained propagation control
Prop propagation only happens between components that share the same props object:
// Props for the main button
const ButtonProps = useStyledProps({
variant: ['primary', 'secondary'],
disabled: Boolean,
})
// Different props for the icon
const IconProps = useStyledProps({
color: ['blue', 'red'],
rounded: Boolean,
})
const ButtonFrame = useStyled('button', {
props: ButtonProps, /* ... */
})
const ButtonText = useStyled('span', {
props: ButtonProps, /* ... */ // ✅ Will receive props from Button
})
const ButtonIcon = useStyled('i', {
props: IconProps, /* ... */ // ❌ Will NOT receive props from Button
})
const Button = useStyledSlots(ButtonFrame, {
slots: {
Text: ButtonText, // Receives Button props
Icon: ButtonIcon, // Does NOT receive Button props
},
})
This allows you to control exactly which components should share props.
Server Components Compatibility
The use-styled
library is designed to work perfectly with React Server Components, without the need to use the 'use client'
directive. This is possible because the library doesn't use React Context or other hooks that only work on the client side.
License
MIT