--- title: "Resources" description: "Host-owned async resources tied to the element lifecycle." group: Signals order: 5 --- # Resources `createResource` ties an async data flow to a host element's lifecycle. It starts observing when the element connects, aborts in-flight work on disconnect, and resumes from the current source on reconnect. ```ts import { createResource } from '@ecopages/radiant'; ``` ## Basic Usage Fetch once on connect — no reactive source needed: ```ts private data = createResource(this, { fetcher: (ctx) => fetch('/api/data', { signal: ctx.signal }).then((r) => r.json()), }); ``` ## Sourced Usage Drive fetches from a reactive source. Source changes abort the previous request and start a new one. `source` only re-runs when it reads a signal (or a host member backed by one). Prefer `@state` / `@prop` so plain property access tracks automatically: ```tsx @state activeCityId = 'venice'; private weatherQuery = createResource(this, { source: (ctx) => ctx.host.activeCityId, fetcher: (cityId, ctx) => fetchWeatherReport(cityId, ctx.signal), onSuccess: (report) => console.log(report), }); override render() { const status = this.weatherQuery.status.get(); const data = this.weatherQuery.data.get(); // ... } ``` You can also pass a raw signal and call `.get()` inside `source`. A plain field is not reactive — changing it will not refetch. Returning a falsy value (`false`, `null`, `undefined`) from `source` disables fetching without clearing the current state. ## Config Options | Option | Type | Description | | :----- | :--- | :---------- | | `fetcher` | `(source, ctx) => Promise` | The async function to call. Without `source`, receives only `ctx`. With `source`, the resolved source value is passed as the first argument. `ctx` provides `host` and `signal`. | | `source` | `(ctx) => Source \| false \| null \| undefined` | A reactive selector that drives re-fetches. Falsy returns pause fetching. | | `initialValue` | `Value` | Seed value exposed from `data` before the first successful resolution. | | `staleTime` | `number` | Milliseconds a successful response stays fresh in the per-instance cache. | | `pendingDelay` | `number` | Milliseconds to wait before transitioning `status` to `'pending'`. | | `onSuccess` | `(data, ctx) => void` | Called after each successful resolution, including cache hits. | | `onError` | `(error, ctx) => void` | Called after each failed resolution. Not called for aborted requests. | | `onSettled` | `(data, error, ctx) => void` | Called after each resolution, whether successful or failed. | ## Return Value `createResource` returns a `HostResource` that implements `AsyncStateResult`: | Property / Method | Type | Description | | :---------------- | :--- | :---------- | | `data` | `ReadonlySignal` | The latest resolved value. | | `status` | `ReadonlySignal<'idle' \| 'pending' \| 'success' \| 'error'>` | Current fetch state. | | `error` | `ReadonlySignal` | The latest rejection reason, if any. | | `refetch()` | `() => void` | Re-runs the fetcher from the current source value. | | `abort()` | `() => void` | Aborts the in-flight request. | ## Lifecycle 1. **Connect** — the resource starts observing the source (or fetches immediately if no source is set). 2. **Source change** — the previous request is aborted and a new one starts. 3. **Disconnect** — the in-flight request is aborted. The current state is preserved. 4. **Reconnect** — the resource resumes from the latest source value. `createResource` registers these hooks automatically via `registerConnectedCallback` and `registerCleanupCallback`. ## Related - See [Weather App](/docs/examples/weather-app) for a full sourced resource example with context. - `createResource` builds on the package-level `asyncState` primitive and adds host lifecycle ownership on top.