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-condition-hook

v1.0.1

Published

A React hook for declarative conditional rendering with fluent API. Provides clean, chainable syntax for complex conditional rendering scenarios with support for boolean conditions, value-based matching, and custom comparators.

Readme

use-condition-hook

npm version TypeScript License: MIT

A React hook for declarative conditional rendering with a fluent, chainable API. Say goodbye to nested ternary operators and complex conditional logic in your JSX!

✨ Features

  • 🎯 Declarative syntax - Clean, readable conditional rendering
  • 🔗 Chainable API - Fluent method chaining for complex scenarios
  • 🎛️ Multiple patterns - Support for when-then, match-case, and fallback patterns
  • 🧩 Custom comparators - Flexible value comparison with built-in utilities
  • 📘 TypeScript first - Full type safety and excellent IDE support
  • 🪶 Lightweight - Minimal bundle size impact (< 5kB)
  • Zero dependencies - Only requires React 16.8+
  • 🛡️ Production ready - Comprehensive error handling and edge case management

📦 Installation

npm install use-condition-hook
yarn add use-condition-hook
pnpm add use-condition-hook

🚀 Quick Start

import { useCondition } from 'use-condition-hook';

function UserProfile({ user, isLoading, error }) {
  const condition = useCondition();

  return condition
    .when(isLoading)
    .then(<div>Loading...</div>)
    .when(error)
    .then(<div className="error">Failed to load profile</div>)
    .when(!user)
    .then(<div>User not found</div>)
    .fallback(<UserDetails user={user} />)
    .otherwise();
}

📚 API Reference

Core Methods

when(condition: boolean)

Creates a conditional branch based on a boolean condition.

condition.when(isLoading).then(<Spinner />)

then(component: React.ReactNode)

Specifies the component to render when the preceding when condition is true.

condition
  .when(user.isAdmin)
  .then(<AdminPanel />)

match(value: any, comparator?: Function)

Initiates a match-case chain for value-based conditional rendering.

condition.match(userRole).case('admin').render(<AdminDashboard />)

case(matchValue: any)

Creates a case branch in a match-case chain.

condition
  .match(theme)
  .case('dark').render(<DarkTheme />)
  .case('light').render(<LightTheme />)

render(component: React.ReactNode)

Specifies the component to render when the preceding case matches.

condition
  .match(status)
  .case('loading').render(<LoadingSpinner />)

fallback(component: React.ReactNode)

Specifies a fallback component when no conditions match.

condition
  .when(isLoading).then(<Spinner />)
  .fallback(<MainContent />)

otherwise(defaultComponent?: React.ReactNode)

Evaluates all conditions and returns the appropriate component.

const result = condition
  .when(isLoading).then(<Spinner />)
  .otherwise(<DefaultContent />);

Utility Methods

reset()

Resets all conditions and state to initial values.

debug()

Logs debug information about current conditions to console.

getMatchedIndex()

Returns the index of the matched condition (-1 if none matched).

hasMatched()

Checks if any condition has been matched.

getConditions()

Returns a read-only copy of all current conditions.

🎨 Usage Patterns

1. Basic When-Then Pattern

Perfect for simple boolean conditions:

function LoadingButton({ isLoading, onClick, children }) {
  const condition = useCondition();

  return (
    <button onClick={onClick} disabled={isLoading}>
      {condition
        .when(isLoading)
        .then(<>🔄 Loading...</>)
        .otherwise(children)
      }
    </button>
  );
}

2. Match-Case Pattern

Ideal for value-based switching:

function StatusBadge({ status }) {
  const condition = useCondition();

  return condition
    .match(status)
    .case('active').render(<span className="badge-green">✅ Active</span>)
    .case('inactive').render(<span className="badge-gray">⏸️ Inactive</span>)
    .case('suspended').render(<span className="badge-red">🚫 Suspended</span>)
    .case('pending').render(<span className="badge-yellow">⏳ Pending</span>)
    .fallback(<span className="badge-blue">❓ Unknown</span>)
    .otherwise();
}

3. Complex Authentication Flow

Handle multiple authentication states:

function AuthenticatedRoute({ user, permissions, children }) {
  const condition = useCondition();

  return condition
    // Check if user is logged in
    .when(!user)
    .then(<LoginForm />)

    // Check email verification
    .when(user && !user.emailVerified)
    .then(<EmailVerificationPrompt />)

    // Check account status
    .when(user?.status === 'suspended')
    .then(<AccountSuspendedMessage />)

    // Check permissions
    .when(user && !permissions.includes('access'))
    .then(<AccessDeniedMessage />)

    // All good, show content
    .fallback(children)
    .otherwise();
}

4. Form Validation States

Handle different validation scenarios:

function FormField({ value, errors, touched, isSubmitting }) {
  const condition = useCondition();

  const fieldClassName = condition
    .when(touched && errors.length > 0)
    .then('form-field error')
    .when(touched && errors.length === 0 && value)
    .then('form-field success')
    .when(isSubmitting)
    .then('form-field submitting')
    .otherwise('form-field');

  const fieldContent = condition
    .reset() // Reset for new chain
    .when(touched && errors.length > 0)
    .then(
      <>
        <input value={value} className={fieldClassName} />
        <span className="error-message">{errors[0]}</span>
      </>
    )
    .otherwise(<input value={value} className={fieldClassName} />);

  return <div className="form-group">{fieldContent}</div>;
}

5. Data Fetching States

Handle API loading states elegantly:

function DataTable({ data, isLoading, error, isEmpty }) {
  const condition = useCondition();

  return (
    <div className="data-container">
      {condition
        .when(isLoading)
        .then(
          <div className="loading-state">
            <Spinner />
            <p>Loading data...</p>
          </div>
        )
        .when(error)
        .then(
          <div className="error-state">
            <ErrorIcon />
            <p>Failed to load data: {error.message}</p>
            <button onClick={() => window.location.reload()}>
              Retry
            </button>
          </div>
        )
        .when(isEmpty)
        .then(
          <div className="empty-state">
            <EmptyIcon />
            <p>No data available</p>
            <button>Add New Item</button>
          </div>
        )
        .fallback(<Table data={data} />)
        .otherwise()
      }
    </div>
  );
}

🔧 Custom Comparators

Built-in Comparators

import { useCondition, comparators } from 'use-condition-hook';

function Example({ score, items, text, user }) {
  const condition = useCondition();

  return condition
    // Strict equality (===)
    .match(user.role, comparators.strict)
    .case('admin').render(<AdminView />)

    // Loose equality (==)
    .match(score, comparators.loose)
    .case('100').render(<PerfectScore />)

    // Deep object comparison
    .match(user, comparators.deepEqual)
    .case({ id: 1, name: 'John' }).render(<WelcomeJohn />)

    // Regex matching
    .match(text, comparators.regex)
    .case(/^hello/i).render(<Greeting />)

    // Array includes
    .match(items, comparators.includes)
    .case('premium').render(<PremiumFeature />)

    // Numeric comparisons
    .match(score, comparators.greaterThan)
    .case(90).render(<HighScore />)

    // Range checking
    .match(score, (val, range) => comparators.range(val, range.min, range.max))
    .case({ min: 80, max: 100 }).render(<GoodRange />)

    .otherwise();
}

Available Comparators

| Comparator | Description | Example | |------------|-------------|---------| | strict | Strict equality (===) | comparators.strict(1, 1) | | loose | Loose equality (==) | comparators.loose(1, '1') | | deepEqual | Deep object comparison | comparators.deepEqual({a:1}, {a:1}) | | regex | Regular expression test | comparators.regex('hello', /^h/) | | includes | Array includes check | comparators.includes([1,2,3], 2) | | greaterThan | Numeric greater than | comparators.greaterThan(5, 3) | | lessThan | Numeric less than | comparators.lessThan(3, 5) | | range | Numeric range check | comparators.range(5, 1, 10) |

Custom Comparator Examples

// Case-insensitive string matching
const caseInsensitive = (a, b) =>
  a.toLowerCase() === b.toLowerCase();

// Array length comparison
const arrayLength = (arr, length) =>
  Array.isArray(arr) && arr.length === length;

// Date comparison
const dateAfter = (date, afterDate) =>
  new Date(date) > new Date(afterDate);

// Usage
condition
  .match(username, caseInsensitive)
  .case('ADMIN').render(<AdminPanel />)

  .match(items, arrayLength)
  .case(0).render(<EmptyList />)

  .match(createdAt, dateAfter)
  .case('2024-01-01').render(<NewPost />);

🎯 Advanced Patterns

Reusable Condition Hooks

Create reusable condition logic:

function useAuthCondition(user) {
  const condition = useCondition();

  return condition
    .when(!user)
    .then(<LoginPrompt />)
    .when(user && !user.emailVerified)
    .then(<EmailVerificationPrompt />)
    .when(user && user.suspended)
    .then(<AccountSuspendedMessage />);
}

function ProfilePage({ user }) {
  const authCondition = useAuthCondition(user);

  return authCondition
    .fallback(<UserProfile user={user} />)
    .otherwise();
}

function SettingsPage({ user }) {
  const authCondition = useAuthCondition(user);

  return authCondition
    .fallback(<UserSettings user={user} />)
    .otherwise();
}

Nested Conditions

Handle complex nested scenarios:

function Dashboard({ user, subscription, features }) {
  const condition = useCondition();

  return condition
    .when(!user)
    .then(<AuthRequired />)
    .when(user && !subscription)
    .then(<SubscriptionRequired />)
    .match(subscription?.tier)
    .case('basic').render(
      condition
        .reset()
        .when(features.basicDashboard)
        .then(<BasicDashboard />)
        .otherwise(<UpgradePrompt />)
    )
    .case('premium').render(<PremiumDashboard />)
    .case('enterprise').render(<EnterpriseDashboard />)
    .fallback(<StandardDashboard />)
    .otherwise();
}

Performance Optimization

// Memoize expensive conditions
function ExpensiveComponent({ data, filters }) {
  const condition = useCondition();

  const shouldShowExpensiveView = useMemo(() =>
    data.length > 1000 && filters.complex
  , [data.length, filters.complex]);

  return condition
    .when(shouldShowExpensiveView)
    .then(<VirtualizedTable data={data} />)
    .otherwise(<SimpleTable data={data} />);
}

🧪 Testing

Testing components that use useCondition:

import { render, screen } from '@testing-library/react';
import { UserProfile } from './UserProfile';

describe('UserProfile', () => {
  it('shows loading state', () => {
    render(<UserProfile isLoading={true} />);
    expect(screen.getByText('Loading...')).toBeInTheDocument();
  });

  it('shows error state', () => {
    const error = new Error('Failed to load');
    render(<UserProfile error={error} />);
    expect(screen.getByText('Failed to load profile')).toBeInTheDocument();
  });

  it('shows user details when loaded', () => {
    const user = { name: 'John Doe', email: '[email protected]' };
    render(<UserProfile user={user} />);
    expect(screen.getByText('John Doe')).toBeInTheDocument();
  });
});

🔍 Debugging

Use the built-in debugging features:

function DebugComponent({ condition }) {
  const cond = useCondition();

  const result = cond
    .when(condition > 0)
    .then(<div>Positive</div>)
    .when(condition < 0)
    .then(<div>Negative</div>)
    .debug() // Logs condition state to console
    .otherwise(<div>Zero</div>);

  // Additional debugging
  console.log('Matched:', cond.hasMatched());
  console.log('Matched index:', cond.getMatchedIndex());
  console.log('All conditions:', cond.getConditions());

  return result;
}

⚠️ Common Pitfalls

1. Forgetting to call otherwise()

// ❌ Wrong - nothing will render
const result = condition.when(true).then(<div />);

// ✅ Correct
const result = condition.when(true).then(<div />).otherwise();

2. Incomplete condition chains

// ❌ Wrong - condition without component
condition.when(true).otherwise(); // Warning logged

// ✅ Correct
condition.when(true).then(<div />).otherwise();

3. Using match() without case()

// ❌ Wrong
condition.match(value).otherwise(); // Warning logged

// ✅ Correct
condition.match(value).case('test').render(<div />).otherwise();

4. Multiple fallbacks

// ❌ Wrong - only last fallback is used
condition
  .fallback(<div>First</div>)   // Ignored
  .fallback(<div>Second</div>)  // Used
  .otherwise();

🔄 Migration Guide

From Ternary Operators

// Before
function Component({ loading, error, data }) {
  return (
    <div>
      {loading ? (
        <Spinner />
      ) : error ? (
        <Error message={error.message} />
      ) : data ? (
        <DataView data={data} />
      ) : (
        <EmptyState />
      )}
    </div>
  );
}

// After
function Component({ loading, error, data }) {
  const condition = useCondition();

  return (
    <div>
      {condition
        .when(loading)
        .then(<Spinner />)
        .when(error)
        .then(<Error message={error.message} />)
        .when(data)
        .then(<DataView data={data} />)
        .otherwise(<EmptyState />)
      }
    </div>
  );
}

From Switch Statements

// Before
function StatusComponent({ status }) {
  const renderStatus = () => {
    switch (status) {
      case 'loading':
        return <Spinner />;
      case 'error':
        return <ErrorMessage />;
      case 'success':
        return <SuccessMessage />;
      default:
        return <DefaultMessage />;
    }
  };

  return <div>{renderStatus()}</div>;
}

// After
function StatusComponent({ status }) {
  const condition = useCondition();

  return (
    <div>
      {condition
        .match(status)
        .case('loading').render(<Spinner />)
        .case('error').render(<ErrorMessage />)
        .case('success').render(<SuccessMessage />)
        .fallback(<DefaultMessage />)
        .otherwise()
      }
    </div>
  );
}

📊 Performance

use-condition-hook is designed for optimal performance:

  • Minimal re-renders: Uses useRef for state management
  • Memoized API: Methods are memoized to prevent unnecessary re-creation
  • Small bundle size: < 5kB minified and gzipped
  • Tree-shakeable: Only import what you use
  • Zero dependencies: No additional bundle weight

🤝 Contributing

Development Setup

git clone https://github.com/carefree-ladka/use-condition-hook.git
cd use-condition-hook
npm install
npm run dev

Running Tests

npm test          # Run tests once
npm run test:watch # Run tests in watch mode
npm run test:coverage # Run tests with coverage

📄 License

MIT © Pawan Kumar

🙏 Acknowledgments

  • Inspired by pattern matching in functional programming languages
  • Built with modern React patterns and TypeScript best practices
  • Thanks to the React community for feedback and suggestions

Happy coding! 🚀

If you find this hook useful, please consider giving it a ⭐ on GitHub!