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

use-logic-rules

v0.1.4

Published

A powerful React hook for reactive rule-based state management with automatic side effects

Readme

use-logic-rules

A powerful React hook for reactive rule-based state management with automatic side effects. Built with TypeScript and powered by signals for optimal performance.

Features

  • 🎯 Rule-based state management - Define business logic as declarative rules
  • Automatic side effects - State updates trigger side effects automatically
  • 🔄 Reactive signals - Built on react-set-signal for fine-grained reactivity
  • 📦 TypeScript first - Full type safety with automatic type inference
  • 🪶 Lightweight - Minimal bundle size
  • 🎨 Flexible - Works with or without reducers

Installation

npm install use-logic-rules react-set-signal
# or
pnpm add use-logic-rules react-set-signal
# or
yarn add use-logic-rules react-set-signal

Basic Usage

import { useLogicRules, Rules } from 'use-logic-rules'

// Define your fact type
interface MyFact {
  count: number
  isEven: boolean
  message: string
}

// Define initial fact
const initialFact: MyFact = {
  count: 0,
  isEven: true,
  message: ''
}

// Define rules with proper typing
const rules: Rules<MyFact> = {
  countIsEven: (fact) => fact.count % 2 === 0,
  countIsPositive: (fact) => fact.count > 0,
}

// Define side effects
const sideEffects = {
  countIsEven: (fact: MyFact, ruleValues) => {
    return {
      isEven: ruleValues.countIsEven,
      message: ruleValues.countIsEven ? 'Even number!' : 'Odd number!'
    }
  }
}

function Counter() {
  const { fact, state, setFact } = useLogicRules({
    initialFact,
    rules,
    sideEffects
  })

  return (
    <div>
      <p>Count: {fact.count}</p>
      <p>Message: {fact.message}</p>
      <p>Is Even: {state.countIsEven ? 'Yes' : 'No'}</p>
      <button onClick={() => setFact(f => { f.count++ })}>
        Increment
      </button>
    </div>
  )
}

Inline Rules (Best IntelliSense)

For the best TypeScript IntelliSense experience, define rules inline:

const { state, fact, setFact } = useLogicRules({
  initialFact: { count: 0, isEven: true, message: '' },
  rules: {
    countIsEven: (fact) => fact.count % 2 === 0,  // fact is automatically typed!
    countIsPositive: (fact) => fact.count > 0,
  }
})

// state.countIsEven and state.countIsPositive are fully typed!

Conditional Rules

You can define conditional rules that only evaluate when a condition is met:

const rules: Rules<MyFact> = {
  countIsEven: (fact) => fact.count % 2 === 0,
  conditionalRule: {
    when: (fact) => fact.count > 0,
    then: {
      isPositive: () => true,
      isLarge: (fact) => fact.count > 100
    }
  }
}

// The state will include: countIsEven, isPositive, isLarge
// (flattened from nested conditional rules)

Using Reducers

You can use reducers to define reusable actions that modify the fact state.

Function Reducer

import { useLogicRules, createReducer } from 'use-logic-rules'

const initialFact = {
  count: 0,
  alarmValue: null as number | null,
}

// Function reducer returns an object of actions
const reducer = createReducer(initialFact, (fact) => ({
  increment: () => fact.set(f => { f.count++ }),
  decrement: () => fact.set(f => { f.count-- }),
  setAlarm: (value: number) => fact.set(f => { f.alarmValue = value }),
  reset: () => fact.set({ count: 0, alarmValue: null })
}))

function Counter() {
  const { fact, state, actions } = useLogicRules({
    initialFact,
    rules,
    reducer
  }, { useReducer: true })

  return (
    <div>
      <p>Count: {fact.count}</p>
      <button onClick={actions.increment}>+</button>
      <button onClick={actions.decrement}>-</button>
      <button onClick={() => actions.setAlarm(10)}>Set Alarm</button>
      <button onClick={actions.reset}>Reset</button>
    </div>
  )
}

Object Reducer

Alternatively, define actions as an object where each action receives fact as the first argument:

import { useLogicRules, createActions } from 'use-logic-rules'

const initialFact = {
  count: 0,
  alarmValue: null as number | null,
}

// Object reducer: each action receives fact as first argument
const reducer = createActions(initialFact, {
  increment: (fact) => fact.set(f => { f.count++ }),
  decrement: (fact) => fact.set(f => { f.count-- }),
  setAlarm: (fact, value: number) => fact.set(f => { f.alarmValue = value }),
  reset: (fact) => fact.set({ count: 0, alarmValue: null })
})

function Counter() {
  const { fact, actions } = useLogicRules({
    initialFact,
    rules,
    reducer
  }, { useReducer: true })

  // Actions are called without the fact argument
  return (
    <div>
      <button onClick={actions.increment}>+</button>
      <button onClick={() => actions.setAlarm(10)}>Set Alarm</button>
    </div>
  )
}

API

useLogicRules(params, options?)

Parameters

  • params: Object

    • initialFact: Initial state of your facts
    • rules: Rule definitions object
    • sideEffects: Side effect handlers (optional)
    • reducer: Optional reducer (function or object) for actions
  • options: UseLogicRulesOptions (optional)

    • debug: Enable debug logging (default: false)
    • useReducer: Enable reducer mode (default: false)
    • delay: Delay for side effects in ms (default: 0)

Returns

  • fact: Current fact state (reactive)
  • state: Current rule values as boolean record (reactive)
  • setFact: Function to update facts
  • $fact: Signal for fact state
  • $ruleValues: Signal for rule values
  • actions: Actions object (when using reducer)

Helper Functions

createRules(initialFact, rules)

Create rules with automatic type inference for the fact parameter. Works in both JS and TypeScript:

import { useLogicRules, createRules } from 'use-logic-rules'

const initialFact = { count: 0, name: '' }

// fact is automatically typed based on initialFact!
const rules = createRules(initialFact, {
  countIsEven: (fact) => fact.count % 2 === 0,
  hasName: (fact) => fact.name.length > 0,
})

const { state } = useLogicRules({ initialFact, rules })

createReducer(initialFact, reducerFn)

Create a function reducer with automatic type inference:

import { createReducer } from 'use-logic-rules'

const initialFact = { count: 0 }

const reducer = createReducer(initialFact, (fact) => ({
  increment: () => fact.set(f => { f.count++ }),
  add: (amount: number) => fact.set(f => { f.count += amount }),
}))

createActions(initialFact, actions)

Create an object reducer with automatic type inference:

import { createActions } from 'use-logic-rules'

const initialFact = { count: 0 }

const reducer = createActions(initialFact, {
  increment: (fact) => fact.set(f => { f.count++ }),
  add: (fact, amount: number) => fact.set(f => { f.count += amount }),
})

defineRules<TFact>()

TypeScript-only helper for defining rules with explicit type parameter:

import { defineRules } from 'use-logic-rules'

interface MyFact {
  count: number
}

const buildRules = defineRules<MyFact>()
const rules = buildRules({
  countIsEven: (fact) => fact.count % 2 === 0,
})

createRulesBuilder(initialFact)

Curried version of createRules:

import { createRulesBuilder } from 'use-logic-rules'

const initialFact = { count: 0 }
const buildRules = createRulesBuilder(initialFact)

const rules = buildRules({
  countIsEven: (fact) => fact.count % 2 === 0,
})

TypeScript Types

The library exports the following user-facing types:

Rules<TFact>

Main type for defining rules objects:

import type { Rules } from 'use-logic-rules'

interface MyFact {
  count: number
  name: string
}

const rules: Rules<MyFact> = {
  countIsPositive: (fact) => fact.count > 0,
  hasName: (fact) => fact.name.length > 0,
}

RuleFunction<TFact>

Type for a simple rule function:

import type { RuleFunction } from 'use-logic-rules'

const myRule: RuleFunction<MyFact> = (fact) => fact.count > 0

ConditionalRule<TFact>

Type for conditional rules with when/then structure:

import type { ConditionalRule } from 'use-logic-rules'

const conditionalRule: ConditionalRule<MyFact> = {
  when: (fact) => fact.count > 0,
  then: {
    isPositive: () => true,
    isLarge: (fact) => fact.count > 100
  }
}

Rule<TFact>

Union type of RuleFunction and ConditionalRule:

import type { Rule } from 'use-logic-rules'

const rule: Rule<MyFact> = (fact) => fact.count > 0
// or
const rule: Rule<MyFact> = {
  when: (fact) => fact.count > 0,
  then: { isPositive: () => true }
}

InferRuleValues<TRules>

Utility type to extract the flattened boolean state type from a rules object:

import type { InferRuleValues } from 'use-logic-rules'

const rules = {
  countIsEven: (fact: MyFact) => fact.count % 2 === 0,
  conditional: {
    when: (fact: MyFact) => fact.count > 0,
    then: {
      isPositive: () => true,
    }
  }
} as const

type MyState = InferRuleValues<typeof rules>
// Result: { countIsEven: boolean; isPositive: boolean }

EnhancedSignal<T>

Signal type with an enhanced set method. Returned by the hook as $fact and $ruleValues:

import type { EnhancedSignal } from 'use-logic-rules'

const { $fact } = useLogicRules({ initialFact, rules })

// Can set directly or with a function
$fact.set({ count: 5, name: 'test' })
$fact.set(prev => { prev.count++ })

UseLogicRulesOptions

Options type for the hook:

import type { UseLogicRulesOptions } from 'use-logic-rules'

const options: UseLogicRulesOptions = {
  debug: true,      // Enable debug logging
  useReducer: true, // Enable reducer mode
  delay: 100,       // Delay for side effects in ms
}

const { state } = useLogicRules({ initialFact, rules }, options)

License

MIT

Repository

https://github.com/slashnot/use-logic-rules