npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@webeach/react-x

v0.1.0

Published

Extended React JSX syntax for conditional classes, inline styles, and CSS variables

Downloads

4

Readme


💎 Особенности

  • 🎨 Условные CSS-классы — добавляйте классы через class:имяКласса с поддержкой CSS Modules
  • 💅 Inline-стили как пропсы — задавайте стили через style:свойство
  • 🔧 CSS-переменные — передавайте CSS custom properties через var:имяПеременной
  • 📋 classList — массив классов вместо конкатенации строк
  • 🏷️ Все HTML/SVG теги — готовые компоненты x.div, x.span, x.svg и т.д.
  • 🔄 HOC для компонентов — оборачивайте свои компоненты через x(Component)
  • 📝 Полная типизация — TypeScript из коробки

📦 Установка

npm install @webeach/react-x

или

pnpm install @webeach/react-x

или

yarn add @webeach/react-x

🚀 Быстрый старт

import { x } from '@webeach/react-x';

function App() {
  const isActive = true;
  const isDisabled = false;

  return (
    <x.div
      class:container={true}
      class:active={isActive}
      class:disabled={isDisabled}
      style:padding="20px"
      style:backgroundColor="#f0f0f0"
      var:primaryColor="#007bff"
    >
      Привет, мир!
    </x.div>
  );
}

Результат в DOM:

<div
  class="container active"
  style="padding: 20px; background-color: #f0f0f0; --primaryColor: #007bff;"
>
  Привет, мир!
</div>

🛠 API

Объект x

Главный экспорт библиотеки. Работает двумя способами:

1. Как коллекция расширенных HTML/SVG тегов

import { x } from '@webeach/react-x';

// HTML теги
<x.div>...</x.div>
<x.span>...</x.span>
<x.button>...</x.button>
<x.input />
<x.form>...</x.form>

// SVG теги
<x.svg>...</x.svg>
<x.path />
<x.circle />
<x.rect />

Поддерживаются все стандартные HTML и SVG теги.

2. Как HOC для оборачивания компонентов

import { x } from '@webeach/react-x';

// Ваш компонент
const ButtonInternal = ({ className, style, children, ...props }) => (
  <button className={className} style={style} {...props}>
    {children}
  </button>
);

// Расширенный компонент
const Button = x(Button);

// Использование
<Button
  class:primary
  class:large={size === 'large'}
  style:borderRadius="8px"
  var:btnColor="blue"
>
  Нажми меня
</Button>

Условные CSS-классы: class:

Добавляйте CSS-классы условно через пропсы с префиксом class:.

<x.div
  class:visible={isVisible}      // добавится если isVisible === true
  class:hidden={!isVisible}      // добавится если isVisible === false
  class:active={isActive}
  class:error={hasError}
  class:my-custom-class          // можно использовать kebab-case
>
  Контент
</x.div>

Поддерживаемые типы значений:

  • booleantrue добавляет класс, false не добавляет
  • string — используется как алиас (имя класса из значения, а не из ключа)
  • null | undefined — класс не добавляется

Использование с CSS Modules

Передавайте строку для использования с CSS Modules — значение становится именем класса:

import styles from './Button.module.css';

<x.button
  class:base={styles.button}           // добавит класс из styles.button
  class:primary={styles.primary}       // добавит класс из styles.primary  
  class:disabled={isDisabled && styles.disabled}  // условно
>
  Нажми меня
</x.button>

Если styles.button = "Button_button__x7f2s", результат:

<button class="Button_button__x7f2s Button_primary__a3bc1">
  Нажми меня
</button>

Важно: При передаче строки используется значение как имя класса, а не ключ после class:.


Массив классов: classList

Альтернативный способ передачи нескольких классов через массив:

const classes = ['card', 'card-primary', isLarge && 'card-large'];

<x.div classList={classes}>
  Контент
</x.div>

Falsy элементы массива автоматически фильтруются.

Можно комбинировать с className и class::

<x.div
  className="base-class"
  classList={['additional', 'classes']}
  class:conditional
>
  Все три способа работают вместе
</x.div>
// Результат: class="base-class additional classes conditional"

Inline-стили: style:

Задавайте CSS-свойства напрямую через пропсы с префиксом style:.

<x.div
  style:display="flex"
  style:justifyContent="center"
  style:alignItems="center"
  style:gap="10px"
  style:padding="20px"
  style:backgroundColor="#f5f5f5"
  style:borderRadius="8px"
>
  Flex контейнер
</x.div>

Преимущества:

  • Автодополнение в IDE для имён CSS-свойств
  • Типизация значений
  • Чистый синтаксис без вложенных объектов

Можно комбинировать с обычным style:

<x.div
  style={{ margin: '10px' }}
  style:padding="20px"
  style:color="red"
>
  Стили объединяются
</x.div>

CSS-переменные: var:

Передавайте CSS custom properties (переменные) через пропсы с префиксом var:.

<x.div
  var:primaryColor="#007bff"
  var:secondaryColor="#6c757d"
  var:spacing="16px"
  var:columns={3}
>
  <x.span style:color="var(--primaryColor)">
    Текст основным цветом
  </x.span>
</x.div>

Результат в DOM:

<div style="--primaryColor: #007bff; --secondaryColor: #6c757d; --spacing: 16px; --columns: 3;">
  <span style="color: var(--primaryColor);">
    Текст основным цветом
  </span>
</div>

Поддерживаемые типы значений:

  • stringvar:color="red"
  • numbervar:columns={3}
  • null — для сброса переменной

Типизация CSS-переменных

Можно типизировать доступные CSS-переменные для компонента:

import { ReactNode } from 'react';
import { x } from '@webeach/react-x';

// Определяем тип переменных
type ThemeVars = {
  primaryColor: string;
  secondaryColor: string;
  spacing: number;
};

// Типизированный компонент
const ThemedBox = x<{ children: ReactNode }, ThemeVars>(
  ({ children, className, style }) => (
    <div className={className} style={style}>
      {children}
    </div>
  )
);

// Теперь IDE подсказывает доступные переменные
<ThemedBox
  var:primaryColor="blue"    // ✓ автодополнение работает
  var:secondaryColor="gray"  // ✓
  var:spacing={16}           // ✓
  var:unknownVar="value"     // ✗ ошибка TypeScript
>
  Контент
</ThemedBox>

📥 Примеры использования

Кнопка с вариантами

import { ReactNode } from 'react';
import { x } from '@webeach/react-x';

type ButtonProps = {
  variant?: 'primary' | 'secondary' | 'danger';
  size?: 'small' | 'medium' | 'large';
  disabled?: boolean;
  children: ReactNode;
};

function Button({ variant = 'primary', size = 'medium', disabled, children }: ButtonProps) {
  return (
    <x.button
      class:btn
      class: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>
  );
}

Кнопка с CSS Modules

import { ReactNode } from 'react';
import { x } from '@webeach/react-x';

import styles from './Button.module.css';

type ButtonProps = {
  variant?: 'primary' | 'secondary';
  disabled?: boolean;
  children: ReactNode;
};

function Button({ variant = 'primary', disabled, children }: ButtonProps) {
  return (
    <x.button
      class: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';

function Card({ title, children, accentColor = '#007bff' }) {
  return (
    <x.article
      class:card
      var: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.h2 style:color="var(--accentColor)" style:marginTop="0">
        {title}
      </x.h2>
      {children}
    </x.article>
  );
}

Адаптивная сетка

import { x } from '@webeach/react-x';

function Grid({ columns = 3, gap = '16px', children }) {
  return (
    <x.div
      style:display="grid"
      style:gridTemplateColumns={`repeat(${columns}, 1fr)`}
      style:gap={gap}
    >
      {children}
    </x.div>
  );
}

SVG иконка

import { x } from '@webeach/react-x';

function Icon({ size = 24, color = 'currentColor' }) {
  return (
    <x.svg
      var: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.path d="M12 2L2 7l10 5 10-5-10-5z" />
      <x.path d="M2 17l10 5 10-5" />
      <x.path d="M2 12l10 5 10-5" />
    </x.svg>
  );
}

📐 Экспортируемые типы

import type { XClassProps, XStyleProps, XVarProps } from '@webeach/react-x';
  • XClassProps — типы для пропсов class:*
  • XStyleProps — типы для пропсов style:* (на основе CSSProperties)
  • XVarProps<T> — типы для пропсов var:* с опциональной типизацией переменных

⚖️ Сравнение с альтернативами

| Возможность | react-x | clsx/classnames | styled-components | |------------------|--------------------------|--------------------------|---------------------------| | Условные классы | class:active={bool} | clsx({ active: bool }) | ${bool && 'active'} | | CSS Modules | class:x={styles.class} | clsx(styles.class) | ❌ | | Inline стили | style:padding="10px" | ❌ | Встроено | | CSS переменные | var:color="red" | ❌ | ${props => props.color} | | TypeScript | ✅ Полный | ✅ | ✅ | | Runtime overhead | Минимальный | Минимальный | CSS-in-JS | | Размер бандла | ~2KB | ~1KB | ~15KB |


🧩 Зависимости

Библиотека не имеет внешних зависимостей (Zero dependencies).


🔖 Выпуск новой версии

Релизы обрабатываются автоматически с помощью semantic-release.

Перед публикацией новой версии убедись, что:

  1. Все изменения закоммичены и запушены в ветку main.
  2. Сообщения коммитов соответствуют формату Conventional Commits:
    • feat: ... — для новых фич
    • fix: ... — для исправлений багов
    • chore: ..., refactor: ... и другие типы — по необходимости
  3. Версионирование определяется автоматически на основе типа коммитов (patch, minor, major).

👨‍💻 Автор

Разработка и поддержка: Руслан Мартынов

Если у тебя есть предложения или найден баг, открывай issue или отправляй pull request.


📄 Лицензия

Этот пакет распространяется под лицензией MIT.