Effector logo

Effector

High-performance, zero-dependency state manager with atomic architecture and static typing.

npm install effector
4.8K56.5K/weekv23.4.41.52 MBMIT148 issues
Last updated: 2025-09-11
Star history chart for zerobias/effector

TL;DR

A powerful, performant state manager that separates business logic from UI components using a multi-store, event-driven architecture.

It provides a rich API for modeling complex data flows with static typing, ensuring maximum type safety and runtime efficiency.

Why Effector?

Effector stands out by strictly enforcing the separation of effects and business logic from the View layer. Unlike hooks-based libraries that define logic inside components, Effector encourages defining logic in a framework-agnostic way, connecting units (stores, events, effects) via declarative operators.

  • Type Safety: Built with TypeScript in mind, offering excellent type inference without requiring manual type annotations for every store or event.
  • Atomic Architecture: State is distributed across multiple stores rather than a single giant tree, allowing for highly efficient updates and code splitting.
  • Declarative Logic: Use operators like sample, merge, and split to describe how data flows and reacts to events, rather than writing imperative callback spaghetti.
  • Performance: It tracks dependencies statically, meaning components only re-render when the specific data they subscribe to changes, with no extra overhead.
  • Framework Agnostic: The core logic can be run in any JavaScript environment (Node.js, Workers), making it ideal for testing logic in isolation.

Code Snippet

Effector allows you to define the "what" and "when" of your application logic completely outside of React components.

import { createStore, createEvent, sample } from 'effector';
import { useUnit } from 'effector-react';

// 1. Define Units (Atoms)
const increment = createEvent();
const reset = createEvent();
const $counter = createStore(0);

// 2. Define Logic (The wiring)
$counter
  .on(increment, (count) => count + 1)
  .reset(reset);

// Declarative dependency: When counter reaches 10, trigger reset
sample({
  clock: increment,
  source: $counter,
  filter: (count) => count >= 10,
  target: reset,
});

// 3. Use in Component
export const Counter = () => {
  // useUnit binds the store and events to the component lifecycle
  const [count, inc, rst] = useUnit([$counter, increment, reset]);

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={inc}>Increment</button>
      <button onClick={rst}>Reset</button>
    </div>
  );
};

In this example, the logic "reset when counter hits 10" is defined separately via sample. The UI component is purely for display and triggering user intents.

Pros and Cons

No library is perfect; understanding the trade-offs is key to selecting the right tool.

Pros

  • Maximum Performance: Fine-grained updates mean your application remains fast even as complexity grows, without manual optimization (like selectors or memo).
  • Best-in-Class TypeScript: The API design allows TS to infer types almost everywhere, reducing the need for generic gymnastics.
  • Testability: Because logic is separated from React components, you can test your entire business flow using simple unit tests.

Cons

  • Steep Learning Curve: The API surface is large (stores, events, effects, domains, samples, guards), and the terminology differs from Redux/React patterns.
  • Verbosity: Connecting simple logic often requires more boilerplate setup than a simple useState or Zustand store.
  • Ecosystem: While high quality, the ecosystem of third-party plugins and the community size is smaller than Redux or Zustand.

Comparison with Other State Management Libraries

The table below outlines the positioning differences between Effector and other popular State Management libraries to help you make an informed decision:

LibraryDesign PhilosophyBest ForPain Points
EffectorAtomic & Event-Driven
Logic is built by connecting atoms via declarative operators.
Scalable Logic
Medium-to-large apps requiring strict logic decoupling and high performance.
Complexity
Requires learning a new mental model and API vocabulary.
ReduxSingle Store
Centralized state with reducers and dispatch actions.
Enterprise Stability
Teams needing strict predictability and established patterns.
Overhead
Can be slow without careful optimization; heavy boilerplate.
JotaiAtomic Hooks
State is broken into atoms, used directly inside React components.
Flexible React
Apps that need granular state but want to stay within the React paradigm.
Logic Scattering
Business logic tends to get mixed into components or custom hooks.

Verdict: When to Adopt

Choose Effector if you are building a medium-to-large scale application where performance and TypeScript support are non-negotiable. It is particularly strong if your team prefers keeping business logic completely separate from UI code (Model-View-ViewModel style). If you want a simple "global variable" for a small app, Effector is likely overkill.