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](https://www.npmjs.com/package/effect-fc) instead.
```bash
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:
```bash
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:
```tsx
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 }) {
yield* Component.useOnMount(() =>
Effect.addFinalizer(() =>
Effect.log("CounterValue unmounted"),
),
)
const [value, setValue] = yield* Lens.useState(count)
return (
)
},
)
const CounterView = Component.make("Counter")(function* () {
const count = yield* Component.useOnMount(() =>
Effect.map(
SubscriptionRef.make(0),
Lens.fromSubscriptionRef,
),
)
const CounterValue = yield* CounterValueView.use
return
})
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 →](https://thila.dev/effect-view/docs/getting-started)
## 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](https://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](https://github.com/Thiladev/effect-view).
## License
[MIT](https://github.com/Thiladev/effect-view/blob/main/LICENSE)