---
sidebar_position: 1
title: Getting Started
---
# Getting Started
`effect-fc` lets React components be written as Effect programs. Inside a
component body you can yield services, run Effects, subscribe to Effect-powered
state, and still export a normal React function component at the edge of your
app.
## Install
Install `effect-fc` alongside `effect` and React 19.2 or newer:
```bash npm2yarn
npm install effect-fc effect react
```
```bash npm2yarn
npm install --save-dev @types/react
```
`effect-fc` is not opinionated about the React platform. Use it with web,
native, custom renderers, or any environment where React components can run.
Then install the platform-specific React packages for your target.
For web apps, install React DOM:
```bash npm2yarn
npm install react-dom
```
```bash npm2yarn
npm install --save-dev @types/react-dom
```
## Create A Runtime
An Effect-FC app needs an Effect runtime. Build one from the services your UI
needs, then share it with React through `ReactRuntime.Provider`.
For an empty app, `Layer.empty` is enough:
```tsx title="src/runtime.ts"
import { Layer } from "effect"
import { ReactRuntime } from "effect-fc"
export const runtime = ReactRuntime.make(Layer.empty)
```
As your app grows, add services to the layer:
```tsx title="src/runtime.ts"
import { FetchHttpClient } from "@effect/platform"
import { Layer } from "effect"
import { ReactRuntime } from "effect-fc"
const AppLive = Layer.empty.pipe(
Layer.provideMerge(FetchHttpClient.layer),
)
export const runtime = ReactRuntime.make(AppLive)
```
## Provide The Runtime
At the React root, wrap your app with `ReactRuntime.Provider`:
```tsx title="src/main.tsx"
import { StrictMode } from "react"
import { createRoot } from "react-dom/client"
import { ReactRuntime } from "effect-fc"
import { App } from "./App"
import { runtime } from "./runtime"
createRoot(document.getElementById("root")!).render(
This component is still running inside Effect-FC.
{message}
}, ) ``` Use `Component.useOnMount` when the component needs a value produced by an Effect during its first render. The value is then cached for the component instance. You can also use it to set up scoped component logic, subscriptions, or resources that should live for the lifetime of that component instance. Use `Component.useOnChange` when render needs the value computed by scoped work that depends on changing inputs. When a dependency changes, React re-renders the component and the hook computes the next value during that render. ```tsx import { Console, Effect } from "effect" import { Component } from "effect-fc" const UserPanelView = Component.make("UserPanel")( function* (props: { readonly userId: string }) { const label = yield* Component.useOnChange( () => Effect.gen(function* () { yield* Console.log(`Preparing view for ${props.userId}`) yield* Effect.addFinalizer(() => Console.log(`Cleaning up ${props.userId}`), ) return `Viewing user ${props.userId}` }), [props.userId], ) return{label}
}, ) ``` In this example, each `userId` gets its own scope. When `userId` changes, Effect-FC closes the previous scope, runs its finalizers, and creates a new scope for the next load. Unlike `useOnMount`, `useOnChange` does not expose the component's root scope directly. It creates and provides its own scope for that dependency window. Some other Effect-FC hooks follow the same pattern when they need a lifecycle that is narrower than the whole component instance. ## Useful Effect-FC Hooks Unlike plain React hooks, Effect-FC hooks return Effects. Use `yield*` to run them inside the component body. The most common Effect-FC hooks are: - `Component.useOnMount`: run an Effect once during the component's first render after mount, and return its value. The Effect must be synchronous. ```tsx const initialData = yield* Component.useOnMount(() => loadInitialData) ``` - `Component.useOnChange`: when a dependency changes, React re-renders the component and the Effect is run again inside the component body. It returns the latest value, so the Effect must be synchronous too. ```tsx const label = yield* Component.useOnChange(() => formatUserLabel(id), [id]) ``` - `Component.useReactEffect`: Effect-powered `React.useEffect` for side effects that do not need to compute render output or trigger a re-render. The Effect must be synchronous and can register scoped finalizers. ```tsx yield* Component.useReactEffect( () => Effect.forkScoped(subscribeToUser(id)), [id], ) ``` - `Component.useReactLayoutEffect`: Effect-powered `React.useLayoutEffect` with scoped finalizers. ```tsx yield* Component.useReactLayoutEffect(() => measure(ref), []) ``` - `Component.useRunSync` and `Component.useRunPromise`: run Effects from React event handlers. ```tsx const runPromise = yield* Component.useRunPromise() return ( ) ``` - `Component.useCallbackSync` and `Component.useCallbackPromise`: create stable React callbacks. ```tsx const save = yield* Component.useCallbackPromise( (user: User) => saveUser(user), [], ) return ``` ## Use React Normally An Effect-FC component is still a React function component. Anything you can do in a regular React component can also be done in an Effect-FC component, including React hooks, refs, event handlers, context, and JSX composition. ```tsx import { Component } from "effect-fc" import * as React from "react" const CounterView = Component.make("Counter")(function* () { const [count, setCount] = React.useState(0) const buttonRef = React.useRef{greeting.greet(props.name)}
}) ``` Service values in the runtime context are reactive by default. If a provided service instance changes, Effect-FC unmounts and mounts again the components that depend on that context, so they read the new service and restart their scoped lifecycle with the new environment. For services that should not trigger that behavior, pass their tags with `Component.withOptions({ nonReactiveTags: [...] })`. ## Provide Services To A Subtree Use `Component.useContextFromLayer` when an Effect-FC component should provide extra services to another Effect-FC component. It turns a `Layer` into a runtime context that can be provided to `.use`. ```tsx title="src/GreetingPageView.tsx" import { Effect } from "effect" import { Component } from "effect-fc" import { GreetingView } from "./Greeting" import { GreetingService } from "./services" const GreetingLive = GreetingService.Default({ greet: (name: string) => `Welcome, ${name}`, }) export const GreetingPageView = Component.make("GreetingPage")(function* () { const context = yield* Component.useContextFromLayer(GreetingLive) const Greeting = yield* Effect.provide(GreetingView.use, context) return