Skip to main content
@craft-ts
LearnLearn with EffectAI agentsGuideReference
@craft-ts/core@craft-ts/component@craft-ts/dev-tools@craft-ts/effect@craft-ts/style@craft-ts/style-testing@craft-ts/i18n@craft-ts/i18n-effect@craft-ts/mcp
ExamplesMigrationEffect compatibility & maturityAdopting CraftTS progressivelyPress kitRoadmapBacklog
Beta
  • Learn
  • Learn with Effect
  • AI agents
  • Guide
  • Reference
  • Packages
  • Resources

i.Learn with Effect

  • Effect users: start here
  • Overview
  • 1. Start with a Craft component
  • 2. Derive UI state
  • 3. Put the domain in Effect
  • 4. Load data with Effect
  • 5. Write data with Effect
  • 6. Provide Layers and route the app
  • 7. Build forms and validate boundaries
  • 8. Test the graph
  • 9. Server functions — POC
  1. Learn with Effect
  2. 2. Derive UI state

2. Derive UI state#

Goal: calculate UI state from the source of truth, and understand the yield* rule that Craft and Effect share.

Use computedEffect for synchronous Effect-backed derivations. Its factory is a generator when it reads a Craft value and returns the Effect to run:

typescript
import { Effect } from 'effect';
import { state } from '@craft-ts/core';
import { computedEffect } from '@craft-ts/effect';

const tasks = yield* state('tasks', [] as Task[]);
const remaining = computedEffect('remaining', function* () {
  const currentTasks = yield* tasks();
  return Effect.succeed(
    currentTasks.filter((task) => !task.done).length,
  );
});

The template can bind remaining directly. computedEffect runs the returned synchronous Effect in place, and Craft re-runs only the binding that depends on it.

The shared dependency vocabulary#

Both runtimes use generators, but they solve different problems:

typescript
const tasks = yield* TaskList();          // Craft service
const access = yield* AccessPolicyService; // Effect service inside an Effect
const value = yield* resource.value();    // Craft reader inside a derivation

The rule is the same: yield what the current function does not own. A Craft factory yields Craft dependencies; an Effect program yields Effect dependencies. The adapter connects the two at a deliberate boundary.

Do not duplicate domain state in the component#

The component should not subscribe to an Effect, convert an Effect to a signal by hand, or start a fiber in a template callback. Those approaches hide loading, cancellation and failure state from Craft. Instead:

  1. Keep the domain operation as Effect<A, E, R>.

  2. Expose it through a Craft Effect-aware primitive.

  3. Derive the display state from the resulting Craft resource.

The next step defines the domain operation and the services it requires.

What you gained#

Derived state that stays reactive and a clear division: Craft derives the UI; Effect composes the domain program.

← 1. Start with a Craft component

3. Put the domain in Effect →

←1. Start with a Craft component3. Put the domain in Effect→

On this page

  • The shared dependency vocabulary
  • Do not duplicate domain state in the component
  • What you gained

@craft-ts · MIT licensed

GitHubnpm

Search