🇺🇸 English version | 🇷🇺 Русская версия
A React extension library that adds convenient syntax for working with CSS classes, styles, and CSS variables directly in JSX.
- 🎨 Conditional CSS classes — add classes via
class:classNamewith CSS Modules support - 💅 Inline styles as props — set styles via
style:property - 🔧 CSS variables — pass CSS custom properties via
var:variableName - 📋 classList — array of classes instead of string concatenation
- 🏷️ All HTML/SVG tags — ready-to-use components
x.div,x.span,x.svg, etc. - 🔄 HOC for components — wrap your components with
x(Component) - 📝 Full TypeScript support — out of the box
npm install @webeach/react-xor
pnpm install @webeach/react-xor
yarn add @webeach/react-ximport{x}from'@webeach/react-x';functionApp(){constisActive=true;constisDisabled=false;return(<x.divclass:container={true}class:active={isActive}class:disabled={isDisabled}style:padding="20px"style:backgroundColor="#f0f0f0"var:primaryColor="#007bff">
Hello, World!
</x.div>);}Result in DOM:
<divclass="container active"
style="padding: 20px; background-color: #f0f0f0; --primaryColor: #007bff;"
>
Hello, World!
</div>The main export of the library. Works in two ways:
import{x}from'@webeach/react-x';// HTML tags<x.div>...</x.div><x.span>...</x.span><x.button>...</x.button><x.input/><x.form>...</x.form>// SVG tags<x.svg>...</x.svg><x.path/><x.circle/><x.rect/>All standard HTML and SVG tags are supported.
import{x}from'@webeach/react-x';// Your componentconstButtonInternal=({ className, style, children, ...props})=>(<buttonclassName={className}style={style}{...props}>{children}</button>);// Extended componentconstButton=x(Button);// Usage<Buttonclass:primaryclass:large={size==='large'}style:borderRadius="8px"var:btnColor="blue">
Click me
</Button>Add CSS classes conditionally via props with the class: prefix.
<x.divclass:visible={isVisible}// added if isVisible === trueclass:hidden={!isVisible}// added if isVisible === falseclass:active={isActive}class:error={hasError}class:my-custom-class// kebab-case is supported>
Content
</x.div>Supported value types:
boolean—trueadds the class,falsedoes notstring— used as an alias (class name from value, not from key)null | undefined— class is not added
Pass a string to use with CSS Modules — the value becomes the class name:
importstylesfrom'./Button.module.css';<x.buttonclass:base={styles.button}// adds class from styles.buttonclass:primary={styles.primary}// adds class from styles.primary class:disabled={isDisabled&&styles.disabled}// conditional>
Click me
</x.button>If styles.button = "Button_button__x7f2s", the result:
<buttonclass="Button_button__x7f2s Button_primary__a3bc1">
Click me
</button>Important: When passing a string, the value is used as the class name, not the key after class:.
An alternative way to pass multiple classes via an array:
constclasses=['card','card-primary',isLarge&&'card-large'];<x.divclassList={classes}>
Content
</x.div>Falsy array elements are automatically filtered out.
Can be combined with className and class::
<x.divclassName="base-class"classList={['additional','classes']}class:conditional>
All three methods work together
</x.div>// Result: class="base-class additional classes conditional"Set CSS properties directly via props with the style: prefix.
<x.divstyle:display="flex"style:justifyContent="center"style:alignItems="center"style:gap="10px"style:padding="20px"style:backgroundColor="#f5f5f5"style:borderRadius="8px">
Flex container
</x.div>Advantages:
- IDE autocompletion for CSS property names
- Typed values
- Clean syntax without nested objects
Can be combined with regular style:
<x.divstyle={{margin: '10px'}}style:padding="20px"style:color="red">
Styles are merged
</x.div>Pass CSS custom properties (variables) via props with the var: prefix.
<x.divvar:primaryColor="#007bff"var:secondaryColor="#6c757d"var:spacing="16px"var:columns={3}><x.spanstyle:color="var(--primaryColor)">
Text in primary color
</x.span></x.div>Result in DOM:
<divstyle="--primaryColor: #007bff; --secondaryColor: #6c757d; --spacing: 16px; --columns: 3;"><spanstyle="color: var(--primaryColor);">
Text in primary color
</span></div>Supported value types:
string—var:color="red"number—var:columns={3}null— to reset a variable
You can type available CSS variables for a component:
import{ReactNode}from'react';import{x}from'@webeach/react-x';// Define variable typestypeThemeVars={primaryColor: string;secondaryColor: string;spacing: number;};// Typed componentconstThemedBox=x<{children: ReactNode},ThemeVars>(({ children, className, style })=>(<divclassName={className}style={style}>{children}</div>));// Now IDE suggests available variables<ThemedBoxvar:primaryColor="blue"// ✓ autocompletion worksvar:secondaryColor="gray"// ✓var:spacing={16}// ✓var:unknownVar="value"// ✗ TypeScript error>
Content
</ThemedBox>import{ReactNode}from'react';import{x}from'@webeach/react-x';typeButtonProps={variant?: 'primary'|'secondary'|'danger';size?: 'small'|'medium'|'large';disabled?: boolean;children: ReactNode;};functionButton({ variant ='primary', size ='medium', disabled, children }: ButtonProps){return(<x.buttonclass:btnclass:btn-primary={variant==='primary'}class:btn-secondary={variant==='secondary'}class:btn-danger={variant==='danger'}class:btn-sm={size==='small'}class:btn-lg={size==='large'}class:disabled={disabled}disabled={disabled}>{children}</x.button>);}import{ReactNode}from'react';import{x}from'@webeach/react-x';importstylesfrom'./Button.module.css';typeButtonProps={variant?: 'primary'|'secondary';disabled?: boolean;children: ReactNode;};functionButton({ variant ='primary', disabled, children }: ButtonProps){return(<x.buttonclass:base={styles.button}class:variant={variant==='primary' ? styles.primary : styles.secondary}class:disabled={disabled&&styles.disabled}disabled={disabled}>{children}</x.button>);}import{x}from'@webeach/react-x';functionCard({ title, children, accentColor ='#007bff'}){return(<x.articleclass:cardvar:accentColor={accentColor}style:borderLeft="4px solid var(--accentColor)"style:padding="20px"style:borderRadius="8px"style:boxShadow="0 2px 8px rgb(0 0 0 / 0.1)"><x.h2style:color="var(--accentColor)"style:marginTop="0">{title}</x.h2>{children}</x.article>);}import{x}from'@webeach/react-x';functionGrid({ columns =3, gap ='16px', children }){return(<x.divstyle:display="grid"style:gridTemplateColumns={`repeat(${columns}, 1fr)`}style:gap={gap}>{children}</x.div>);}import{x}from'@webeach/react-x';functionIcon({ size =24, color ='currentColor'}){return(<x.svgvar:iconSize={`${size}px`}var:iconColor={color}style:width="var(--iconSize)"style:height="var(--iconSize)"viewBox="0 0 24 24"fill="none"stroke="var(--iconColor)"strokeWidth="2"><x.pathd="M12 2L2 7l10 5 10-5-10-5z"/><x.pathd="M2 17l10 5 10-5"/><x.pathd="M2 12l10 5 10-5"/></x.svg>);}importtype{XClassProps,XStyleProps,XVarProps}from'@webeach/react-x';XClassProps— types forclass:*propsXStyleProps— types forstyle:*props (based onCSSProperties)XVarProps<T>— types forvar:*props with optional variable typing
| Feature | react-x | clsx/classnames | styled-components |
|---|---|---|---|
| Conditional classes | class:active={bool} | clsx({ active: bool }) | ${bool && 'active'} |
| CSS Modules | class:x={styles.class} | clsx(styles.class) | ❌ |
| Inline styles | style:padding="10px" | ❌ | Built-in |
| CSS variables | var:color="red" | ❌ | ${props => props.color} |
| TypeScript | ✅ Full | ✅ | ✅ |
| Runtime overhead | Minimal | Minimal | CSS-in-JS |
| Bundle size | ~2KB | ~1KB | ~15KB |
This library has no external dependencies (Zero dependencies).
Releases are handled automatically via semantic-release.
Before publishing a new version, ensure that:
- All changes are committed and pushed to the
mainbranch. - Commit messages follow the Conventional Commits format:
feat: ...— for new featuresfix: ...— for bug fixeschore: ...,refactor: ...and other types — as needed
- Versioning is determined automatically based on commit types (
patch,minor,major).
Development and maintenance: Ruslan Martynov
If you have suggestions or found a bug, open an issue or submit a pull request.
This package is distributed under the MIT License.