Files
effect-view/packages/effect-fc-next
Julien Valverdé 3533fa0c84
Lint / lint (push) Failing after 39s
Remove ErrorObserver
2026-07-25 22:48:42 +02:00
..
2026-07-25 22:48:42 +02:00
2026-07-25 22:27:11 +02:00
2026-06-22 02:04:20 +02:00
2026-07-25 22:48:42 +02:00
2026-07-22 20:56:51 +02:00
2026-06-22 02:04:20 +02:00
Fix
2026-07-22 21:41:26 +02:00

Effect View logo

Effect View

Write React components as typed Effect programs.

npm version Effect View documentation MIT license

Read the documentation →

Effect View brings Effect's typed services, resource safety, concurrency, and data modeling into React 19 without replacing React's component model. Yield Effects and services from a component body, let scopes follow the React lifecycle, and return ordinary JSX.

Effect v4 beta: Effect View is built for the Effect v4 beta release. If your application uses Effect v3, use the legacy effect-fc package instead.

npm install effect-view effect@beta react

Effect View does not depend on react-dom. Install the renderer used by your application—for example, react-dom for the web:

npm install react-dom

You can use Effect View with React Native or any other React renderer instead.

What it looks like

An Effect View component is an Effect program that produces JSX. It can create scoped state, access services, and use React-facing hooks without leaving the generator model:

import { Effect, SubscriptionRef } from "effect"
import { Component, Lens } from "effect-view"
import { runtime } from "./runtime"

const CounterValueView = Component.make("CounterValue")(
  function* ({ count }: { readonly count: Lens.Lens<number> }) {
    yield* Component.useOnMount(() =>
      Effect.addFinalizer(() =>
        Effect.log("CounterValue unmounted"),
      ),
    )

    const [value, setValue] = yield* Lens.useState(count)

    return (
      <button onClick={() => setValue((n) => n + 1)}>
        Count: {value}
      </button>
    )
  },
)

const CounterView = Component.make("Counter")(function* () {
  const count = yield* Component.useOnMount(() =>
    Effect.map(
      SubscriptionRef.make(0),
      Lens.fromSubscriptionRef,
    ),
  )

  const CounterValue = yield* CounterValueView.use

  return <CounterValue count={count} />
})

export const Counter = CounterView.pipe(
  Component.withContext(runtime.context),
)

CounterView composes another Effect View component by yielding its .use Effect, then renders the resulting component as ordinary JSX. The child can access the current Effect context and receives its own component scope.

Effect.addFinalizer uses the scope already provided to the component body, so the finalizer above runs when CounterValue unmounts.

At the outer boundary, Counter is still a normal React component and its Effect requirements remain typed until they reach the runtime.

Build the runtime and render your first component →

Why Effect View

Capability What it gives you
Effect components Component.make Yield Effects and typed services directly, then return JSX.
Managed lifecycles Scope.Scope Finalizers, fibers, subscriptions, and resources close with their component or dependency lifecycle.
One application runtime ReactRuntime Build your Layer once and make its services available throughout the UI.
Reactive state Lens and View Focus large state models into small writable values and subscribe only where React needs them.
Server state Query and Mutation TanStack Query-style caching, invalidation, staleness, and mutations with typed Effects underneath.
Schema-driven forms MutationForm and LensForm Keep input-friendly encoded values while application code receives validated, decoded types.
Async rendering Async and Memoized Integrate asynchronous Effects with Suspense while retaining Effect errors and cancellation.

Designed for both ecosystems

Effect View does not hide Effect behind callbacks or recreate React around a new rendering model. React still owns rendering, JSX, hooks, Suspense, and events. Effect owns services, errors, resource safety, concurrency, streams, and schemas. Effect View provides the lifecycle-aware boundary between them.

Documentation

The complete documentation is available at thila.dev/effect-view.

Requirements

  • React 19.2 or newer
  • Effect v4 beta (effect@beta)
  • TypeScript and @types/react for TypeScript projects

Effect View is renderer-independent and does not require react-dom.

Project status

Effect View is currently beta software. The main APIs are available, but breaking changes and rough edges are still possible before a stable release. React Fast Refresh does not yet recognize Effect View component definitions, so changes to them currently require a page reload; ordinary React components are unaffected (currently being worked on).

Issues, ideas, and contributions are welcome on GitHub.

License

MIT