Create complex business rules with an intuitive drag-and-drop interface. No more JSON wrestling - build rules visually and export to your rule engine.
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" }
}
]
};
<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);
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' }
];
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}
onRuleChange={setRule}
availableFields={fields}
testData={testData}
theme="dark"
showPreview={true}
showHistory={true}
customOperators={operators}
onValidationError={onError}
className="my-rule-builder"
/>
RuleEvaluator
Real-time rule evaluation with visual feedback.
<RuleEvaluator
rule={rule}
testData={testData}
engine={RuleEngine}
onResult={handleResult}
showSteps={true}
highlightActive={true}
/>
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
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:
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
const availableFields = useMemo(() =>
generateFieldsFromSchema(schema), [schema]
);
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;
};
}
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
git clone https://github.com/ali-master/rule-engine.git
cd rule-engine/packages/builder
pnpm install
pnpm dev
pnpm test
pnpm build
π Why Choose This 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