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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@usex/rule-engine-builder

v0.0.3

Published

Visual rule construction toolkit for React applications. Drag, drop, and compose complex business logic without writing JSON. Transform IF-THEN decisions into intuitive visual flows with real-time evaluation, undo/redo history, and TypeScript-powered inte

Readme


Create complex business rules with an intuitive drag-and-drop interface. No more JSON wrestling - build rules visually and export to your rule engine.

// Instead of writing complex JSON...
const rule = {
  conditions: [
    {
      and: [
        { field: "$.user.tier", operator: "equals", value: "premium" },
        { field: "$.order.total", operator: "greater-than", value: 100 }
      ],
      result: { discount: 0.15, shipping: "free" }
    }
  ]
};

// Just drag, drop, and configure visually! 🎨
<RuleBuilder 
  onRuleChange={setRule}
  availableFields={fields}
  theme="dark"
/>

🚀 Why Visual Builder?

Intuitive Visual Interface

  • 🎨 Drag & Drop - Build rules by dragging components onto a canvas
  • 🌲 Tree Structure - Visual representation of complex nested logic
  • 📦 Component Palette - Pre-built operators, fields, and logic blocks
  • 🎯 Drop Zones - Smart targeting for precise rule construction

Developer-Friendly Experience

  • Real-time Preview - See JSON output as you build
  • 🔄 Live Evaluation - Test rules instantly with sample data
  • 📈 History Management - Undo/redo with 100-entry history
  • ⌨️ Keyboard Shortcuts - Professional keyboard navigation

Production-Ready Features

  • 🛡️ TypeScript Native - Full type safety and IntelliSense support
  • 🎨 Theme System - Light/dark modes with full customization
  • Accessible - WCAG compliant with screen reader support
  • 📱 Responsive - Works on desktop, tablet, and mobile devices

Extensible & Customizable

  • 🔧 Custom Operators - Add your own business-specific operators
  • 🎛️ Field Discovery - Auto-suggest fields from your data schema
  • 🌈 Custom Themes - Match your application's design system
  • 📊 Export Options - JSON, TypeScript, or custom formats

🎬 Quick Start

npm install @usex/rule-engine-builder @usex/rule-engine react

Basic Usage

import React, { useState } from 'react';
import { RuleBuilder } from '@usex/rule-engine-builder';
import { RuleEngine } from '@usex/rule-engine';

function App() {
  const [rule, setRule] = useState(null);
  
  // Define available fields for your users
  const availableFields = [
    { name: '$.user.tier', type: 'string', label: 'User Tier' },
    { name: '$.user.age', type: 'number', label: 'User Age' },
    { name: '$.order.total', type: 'number', label: 'Order Total' },
    { name: '$.order.items', type: 'array', label: 'Order Items' }
  ];

  // Test data for live evaluation
  const testData = {
    user: { tier: 'premium', age: 28 },
    order: { total: 150, items: ['laptop', 'mouse'] }
  };

  return (
    <div className="app">
      <h1>Build Your Business Rules</h1>
      
      <RuleBuilder
        rule={rule}
        onRuleChange={setRule}
        availableFields={availableFields}
        testData={testData}
        theme="auto"
        showPreview={true}
        showHistory={true}
      />
      
      {rule && (
        <div className="rule-output">
          <h3>Generated Rule:</h3>
          <pre>{JSON.stringify(rule, null, 2)}</pre>
          
          <button onClick={() => testRule()}>
            Test Rule
          </button>
        </div>
      )}
    </div>
  );

  async function testRule() {
    if (!rule) return;
    
    const result = await RuleEngine.evaluate(rule, testData);
    console.log('Rule Result:', result);
  }
}

🏗️ Core Components

RuleBuilder (Main Component)

The primary visual rule construction interface.

<RuleBuilder
  rule={rule}                    // Current rule state
  onRuleChange={setRule}         // Callback when rule changes
  availableFields={fields}       // Available fields for selection
  testData={testData}           // Sample data for live testing
  theme="dark"                  // Theme: 'light' | 'dark' | 'auto'
  showPreview={true}            // Show JSON preview panel
  showHistory={true}            // Enable undo/redo functionality
  customOperators={operators}    // Custom business operators
  onValidationError={onError}   // Validation error callback
  className="my-rule-builder"   // Custom CSS classes
/>

RuleEvaluator

Real-time rule evaluation with visual feedback.

<RuleEvaluator
  rule={rule}
  testData={testData}
  engine={RuleEngine}
  onResult={handleResult}
  showSteps={true}              // Show evaluation steps
  highlightActive={true}        // Highlight active rule paths
/>

ModernConstraintEditor

Advanced constraint editing with intelligent suggestions.

<ModernConstraintEditor
  constraint={constraint}
  onConstraintChange={setConstraint}
  availableFields={fields}
  operators={operators}
  showFieldSuggestions={true}
  allowCustomFields={true}
/>

🔧 Component Showcase

Building Blocks

The visual builder provides intuitive components for every rule element:

Logic Operators

  • AND - All conditions must be true
  • OR - Any condition must be true
  • NONE - No conditions must be true

Comparison Operators

  • Equals (=) - Exact value matching
  • Greater Than (>) - Numeric comparison
  • Contains - Array/string inclusion
  • Matches - Regular expression patterns

Field Selectors

  • JSONPath Fields - $.user.profile.name
  • Nested Properties - Deep object navigation
  • Array Elements - $.items[0].price
  • Custom Fields - User-defined properties

Value Inputs

  • Static Values - Fixed strings, numbers, booleans
  • Dynamic References - $.other.field
  • Arrays - Multiple value selection
  • Date/Time - Calendar and time pickers

🎯 Real-World Examples

E-commerce Discount Builder

function DiscountRuleBuilder() {
  const [discountRule, setDiscountRule] = useState(null);
  
  const ecommerceFields = [
    { name: '$.customer.tier', type: 'string', label: 'Customer Tier', 
      options: ['bronze', 'silver', 'gold', 'platinum'] },
    { name: '$.cart.total', type: 'number', label: 'Cart Total' },
    { name: '$.cart.itemCount', type: 'number', label: 'Number of Items' },
    { name: '$.customer.isFirstOrder', type: 'boolean', label: 'First Order' },
    { name: '$.promotions.active', type: 'array', label: 'Active Promotions' }
  ];

  const customOperators = [
    {
      name: 'is-weekend',
      label: 'Is Weekend',
      category: 'datetime',
      description: 'Check if current date is weekend'
    },
    {
      name: 'bulk-discount-eligible',
      label: 'Bulk Discount Eligible',
      category: 'business',
      description: 'Check if order qualifies for bulk pricing'
    }
  ];

  return (
    <div className="discount-builder">
      <h2>Discount Rule Builder</h2>
      
      <RuleBuilder
        rule={discountRule}
        onRuleChange={setDiscountRule}
        availableFields={ecommerceFields}
        customOperators={customOperators}
        theme="light"
        resultTemplate={{
          discount: 0,
          code: '',
          message: '',
          expires: null
        }}
      />
    </div>
  );
}

User Access Control Builder

function AccessControlBuilder() {
  const [accessRule, setAccessRule] = useState(null);
  
  const accessFields = [
    { name: '$.user.role', type: 'string', label: 'User Role' },
    { name: '$.user.department', type: 'string', label: 'Department' },
    { name: '$.user.clearanceLevel', type: 'number', label: 'Clearance Level' },
    { name: '$.resource.sensitivity', type: 'string', label: 'Resource Sensitivity' },
    { name: '$.session.duration', type: 'number', label: 'Session Duration' },
    { name: '$.time.currentHour', type: 'number', label: 'Current Hour' }
  ];

  return (
    <div className="access-builder">
      <h2>Access Control Rules</h2>
      
      <RuleBuilder
        rule={accessRule}
        onRuleChange={setAccessRule}
        availableFields={accessFields}
        theme="dark"
        showHistory={true}
        resultTemplate={{
          allowed: false,
          permissions: [],
          expires: null,
          reason: ''
        }}
      />
      
      <RuleEvaluator
        rule={accessRule}
        testData={sampleUserSession}
        showSteps={true}
        onResult={(result) => {
          console.log('Access Decision:', result);
        }}
      />
    </div>
  );
}

Form Validation Builder

function ValidationBuilder() {
  const [validationRules, setValidationRules] = useState([]);
  
  const formFields = [
    { name: 'email', type: 'string', label: 'Email Address' },
    { name: 'password', type: 'string', label: 'Password' },
    { name: 'confirmPassword', type: 'string', label: 'Confirm Password' },
    { name: 'age', type: 'number', label: 'Age' },
    { name: 'country', type: 'string', label: 'Country' },
    { name: 'acceptTerms', type: 'boolean', label: 'Accept Terms' }
  ];

  return (
    <div className="validation-builder">
      <h2>Form Validation Rules</h2>
      
      {validationRules.map((rule, index) => (
        <div key={index} className="validation-rule">
          <h3>Rule {index + 1}</h3>
          
          <RuleBuilder
            rule={rule}
            onRuleChange={(newRule) => {
              const updated = [...validationRules];
              updated[index] = newRule;
              setValidationRules(updated);
            }}
            availableFields={formFields}
            mode="validation"
            showPreview={false}
          />
        </div>
      ))}
      
      <button onClick={() => addValidationRule()}>
        Add Validation Rule
      </button>
    </div>
  );
}

🎨 Advanced Features

Custom Themes

const customTheme = {
  colors: {
    primary: '#6366f1',
    secondary: '#10b981',
    background: '#f8fafc',
    surface: '#ffffff',
    text: '#1f2937',
    border: '#e5e7eb'
  },
  spacing: {
    sm: '0.5rem',
    md: '1rem',
    lg: '1.5rem'
  },
  borderRadius: '0.75rem',
  shadows: {
    sm: '0 1px 2px rgba(0, 0, 0, 0.05)',
    md: '0 4px 6px rgba(0, 0, 0, 0.1)'
  }
};

<RuleBuilder
  rule={rule}
  onRuleChange={setRule}
  theme={customTheme}
  availableFields={fields}
/>

Field Discovery

// Auto-discover fields from your data schema
const fields = useFieldDiscovery(sampleData, {
  maxDepth: 3,
  includeArrays: true,
  typeInference: true
});

<RuleBuilder
  rule={rule}
  onRuleChange={setRule}
  availableFields={fields}
  allowFieldDiscovery={true}
  onFieldDiscovered={(field) => {
    console.log('New field discovered:', field);
  }}
/>

History Management

function RuleBuilderWithHistory() {
  const [rule, setRule] = useState(null);
  const { history, undo, redo, canUndo, canRedo } = useRuleHistory();

  return (
    <div>
      <div className="history-controls">
        <button 
          onClick={undo} 
          disabled={!canUndo}
          title="Undo (Ctrl+Z)"
        >
          ↶ Undo
        </button>
        
        <button 
          onClick={redo} 
          disabled={!canRedo}
          title="Redo (Ctrl+Y)"
        >
          ↷ Redo
        </button>
        
        <span className="history-count">
          Step {history.currentIndex + 1} of {history.entries.length}
        </span>
      </div>

      <RuleBuilder
        rule={rule}
        onRuleChange={setRule}
        showHistory={true}
        maxHistoryEntries={100}
      />
    </div>
  );
}

Keyboard Shortcuts

The builder supports professional keyboard navigation:

| Shortcut | Action | |----------|--------| | Ctrl+Z | Undo last change | | Ctrl+Y | Redo last undone change | | Ctrl+D | Duplicate selected component | | Delete | Remove selected component | | Tab | Navigate between components | | Enter | Edit selected component | | Escape | Cancel current operation | | Ctrl+S | Export rule (custom handler) |

🏎️ Performance & Optimization

Virtual Scrolling

For large rule sets, the builder uses virtual scrolling:

<RuleBuilder
  rule={complexRule}
  onRuleChange={setRule}
  virtualScrolling={true}
  itemHeight={60}
  maxVisibleItems={50}
/>

Lazy Loading

Components are loaded on-demand for better performance:

const LazyRuleBuilder = lazy(() => import('@usex/rule-engine-builder'));

function App() {
  return (
    <Suspense fallback={<div>Loading rule builder...</div>}>
      <LazyRuleBuilder />
    </Suspense>
  );
}

Optimized Rendering

// Use memo for expensive field calculations
const availableFields = useMemo(() => 
  generateFieldsFromSchema(schema), [schema]
);

// Debounce rule changes to reduce re-renders
const debouncedOnChange = useMemo(
  () => debounce(setRule, 300),
  []
);

<RuleBuilder
  rule={rule}
  onRuleChange={debouncedOnChange}
  availableFields={availableFields}
  optimizeRendering={true}
/>

🎓 TypeScript Support

Full type safety for all components and props:

interface CustomField {
  name: string;
  type: 'string' | 'number' | 'boolean' | 'array' | 'object';
  label: string;
  description?: string;
  options?: string[];
  validation?: {
    required?: boolean;
    min?: number;
    max?: number;
    pattern?: string;
  };
}

interface CustomRule<T = any> {
  id: string;
  conditions: Condition<T>[];
  result?: T;
  metadata?: {
    name: string;
    description: string;
    created: Date;
    modified: Date;
  };
}

// Type-safe rule builder
const Builder = () => {
  const [rule, setRule] = useState<CustomRule<DiscountResult>>(null);
  
  return (
    <RuleBuilder<DiscountResult>
      rule={rule}
      onRuleChange={setRule}
      availableFields={typedFields}
      resultType="discount"
    />
  );
};

🧪 Testing Components

import { render, screen, fireEvent } from '@testing-library/react';
import { RuleBuilder } from '@usex/rule-engine-builder';

describe('RuleBuilder', () => {
  const mockFields = [
    { name: 'age', type: 'number', label: 'Age' },
    { name: 'country', type: 'string', label: 'Country' }
  ];

  it('should render field palette', () => {
    render(
      <RuleBuilder
        availableFields={mockFields}
        onRuleChange={jest.fn()}
      />
    );
    
    expect(screen.getByText('Age')).toBeInTheDocument();
    expect(screen.getByText('Country')).toBeInTheDocument();
  });

  it('should handle drag and drop', () => {
    const onChange = jest.fn();
    
    render(
      <RuleBuilder
        availableFields={mockFields}
        onRuleChange={onChange}
      />
    );
    
    const ageField = screen.getByText('Age');
    const dropZone = screen.getByTestId('drop-zone');
    
    fireEvent.dragStart(ageField);
    fireEvent.drop(dropZone);
    
    expect(onChange).toHaveBeenCalledWith(
      expect.objectContaining({
        conditions: expect.arrayContaining([
          expect.objectContaining({
            field: 'age'
          })
        ])
      })
    );
  });
});

📚 Documentation & Resources

🤝 Contributing

We welcome contributions! Whether it's:

  • 🐛 Bug reports and fixes
  • ✨ New components or features
  • 📖 Documentation improvements
  • 🎨 Theme contributions

See our Contributing Guide for details.

Development Setup

# Clone the repository
git clone https://github.com/ali-master/rule-engine.git
cd rule-engine/packages/builder

# Install dependencies
pnpm install

# Start development server
pnpm dev

# Run tests
pnpm test

# Build for production
pnpm build

🆚 Why Choose This Builder?

| Feature | @usex/rule-engine-builder | React QueryBuilder | React Awesome Query Builder | |---------|---------------------------|--------------------|-----------------------------| | TypeScript Native | ✅ | ⚠️ Partial | ⚠️ Partial | | Drag & Drop | ✅ | ❌ | ✅ | | Real-time Evaluation | ✅ | ❌ | ❌ | | History/Undo | ✅ | ❌ | ❌ | | Custom Themes | ✅ | ⚠️ Limited | ✅ | | Mobile Responsive | ✅ | ⚠️ Partial | ❌ | | JSONPath Support | ✅ | ❌ | ❌ | | Bundle Size | 45KB | 120KB | 180KB | | Tree Visualization | ✅ | ❌ | ✅ | | Keyboard Shortcuts | ✅ | ❌ | ❌ |

📄 License

MIT © Ali Torki


Built with ❤️ for modern React applications

⭐ Star us on GitHub🐛 Report Issues💬 Discussions