---
title: .derived()
description: Declare computed values that depend on state or input, with built-in loading and error envelopes.
order: 203
---

import { Preview } from "$lib/components/preview";

export const example = `import ilha from "ilha";
import { Input } from "areia";

export default ilha
  .state("price", 100)
  .state("qty", 3)
  .derived("total", ({ state }) => {
    return state.price() * state.qty();
  })
  .render(({ state, derived }) => (
    <div class="flex flex-col gap-2">
      <Input
        label="Price"
        type="number"
        bind:value={state.price}
      />
      <Input
        label="Quantity"
        type="number"
        bind:value={state.qty}
      />
      <p>Total: {derived.total()}</p>
    </div>
  ));
`

# Derived

Declares a computed value that depends on state or input. Derived values can be synchronous or async, and they re-run automatically when any reactive dependency changes.

[Interactive Tutorial](/tutorial/counter/derived)

## Basic usage

<Preview code={example} size="lg" />

Each derived entry is a signal accessor — call it with no arguments to read the resolved value, the same way you read `state.count()`:

```ts
derived.total(); // read → returns current value
```

When any signal the derived function reads changes, the function re-runs and the island re-renders.

## Reading and writing

Derived accessors can also be written for optimistic UI. A write updates the value immediately without waiting for the derived function to re-run:

```ts
derived.total(999); // write → sets value optimistically
```

```tsx twoslash
import ilha from "ilha";

const Cart = ilha
  .state("price", 100)
  .state("qty", 3)
  .derived("total", ({ state }) => state.price() * state.qty())
  .on("button@click", ({ derived }) => derived.total(0))
  .render(({ derived }) => <p>Total: {derived.total()}</p>);
```

The next time the derived function runs (for example after state changes or an async fetch resolves), it overwrites the optimistic value with the computed result.

## The derived envelope

Every derived value also exposes `loading`, `value`, and `error` on the accessor itself. Use these when you need explicit control — especially with async derived values:

| Property  | Type                 | Description                           |
| --------- | -------------------- | ------------------------------------- |
| `loading` | `boolean`            | `true` while the function is running  |
| `value`   | `T \| undefined`     | The last successfully resolved value  |
| `error`   | `Error \| undefined` | Set if the function threw or rejected |

```ts
derived.total(); // same as derived.total.value when resolved
derived.total.value; // envelope read
derived.total.loading; // false for sync derived after first run
derived.total.error; // undefined when no error
```

For synchronous derived values, `loading` is `false` after the first run and `()` returns the computed value directly. For async derived values, check `loading` and `error` before reading `value` or calling `()`.

## Async derived values

Pass an async function to fetch data or run any other asynchronous work. The envelope tracks progress while the promise is pending:

```tsx twoslash
import ilha from "ilha";

const UserCard = ilha
  .state("userId", 1)
  // [!code highlight:4]
  .derived("user", async ({ state, signal }) => {
    const res = await fetch(`/api/users/${state.userId()}`, {
      signal,
    });
    return res.json();
  })
  .render(({ derived }) => {
    if (derived.user.loading) return <p>Loading…</p>;
    if (derived.user.error)
      return <p>Error: {derived.user.error.message}</p>;
    return <p>{derived.user().name}</p>;
  });
```

On first render, `loading` is `true` and `derived.user()` is `undefined` until the promise resolves.

## Reactive dependencies

The derived function re-runs whenever any signal it reads changes. Dependencies are tracked automatically — you do not need to declare them manually.

```tsx twoslash
import ilha from "ilha";

const Search = ilha
  .state("query", "")
  .derived("results", async ({ state, signal }) => {
    const res = await fetch(`/api/search?q=${state.query()}`, {
      signal,
    });
    return res.json() as Promise<string[]>;
  })
  .render(({ state, derived }) => (
    <>
      <input value={state.query()} />
      {derived.results.loading ? (
        <p>Searching…</p>
      ) : (
        <ul>
          {derived.results()?.map((r) => (
            <li>{r}</li>
          ))}
        </ul>
      )}
    </>
  ));
```

## Abort signal

Every async derived function receives an `AbortSignal` that aborts when the function is about to re-run. Pass it to `fetch` or any other cancellable API to avoid stale responses:

```tsx twoslash
import ilha from "ilha";

const Island = ilha
  .state("id", 1)
  .derived("data", async ({ state, signal }) => {
    const res = await fetch(`/api/items/${state.id()}`, {
      signal,
    });
    return res.json();
  })
  .render(({ derived }) => (
    <p>{derived.data()?.name ?? "…"}</p>
  ));
```

If the signal was already aborted before your async work completes, the result is discarded silently.

## Keeping stale value during reload

When a derived function re-runs, `loading` becomes `true` but `value` retains the previous result until the new one resolves. This lets you avoid layout shifts by showing stale content while refreshing:

```tsx twoslash
import ilha from "ilha";

const Island = ilha
  .state("page", 1)
  .derived("items", async ({ state, signal }) => {
    const res = await fetch(`/api/items?page=${state.page()}`, {
      signal,
    });
    return res.json() as Promise<string[]>;
  })
  .render(({ derived }) => (
    <>
      <ul
        style={`opacity: ${derived.items.loading ? "0.5" : "1"}`}
      >
        {derived.items()?.map((i) => (
          <li>{i}</li>
        ))}
      </ul>
      <button>Next page</button>
    </>
  ));
```

## SSR behavior

During SSR, derived functions are called once. If they are async, the island awaits them before rendering when called as `await island(props)`. When called synchronously via `island.toString()`, async derived values render with `loading: true` immediately.

```ts
// Async — waits for all derived values to resolve
const html = await MyIsland({ userId: 1 });

// Sync — derived renders in loading state
const html = MyIsland.toString({ userId: 1 });
```

## Hydration snapshots

When using `.hydratable()` with `snapshot: true`, derived values are embedded in the server output and restored on the client. This means the island can render immediately on mount without re-fetching, using the server-resolved value as the initial state.

See [`.hydratable()`](/guide/island/hydratable) for full snapshot options.

## Notes

- Derived keys must be unique within the same builder chain.
- **Rejected or thrown async derived work** sets `derived.key.error` on the envelope — it is **not** routed to [`.onError()`](/guide/island/onerror). Handle failures in the render path via `error` / `loading`.
- Async schemas are not supported as derived functions — the function itself can be async, but ilha [`.input()`](/guide/island/input) schemas must remain synchronous.
- Multiple derived entries are independent. Each tracks its own dependencies and re-runs on its own schedule.
