# ⚡ spark-html-query
Declarative async data for [spark-html](https://www.npmjs.com/package/spark-html)
— a **self-fetching reactive store**. One dependency (`spark-html`), built
entirely on its `store()`.
A `query` runs an async function and exposes the result as reactive store state.
Any component reads it with the same `useStore` it already knows, and re-renders
as the request settles — no `onMount`, no manual `loading` flags, no `fetch`
boilerplate.
```js
import { query } from 'spark-html-query';
query('user', () => fetch('/api/user').then((r) => r.json()));
```
```html
Loading…
Failed: {user.error.message}
{user.data?.name}
```
## Install
```bash
npm install spark-html-query
```
## State
`useStore(name)` returns a reactive object:
| Key | Meaning |
|-----|---------|
| `data` | The latest resolved value (or `initialData` / `null` before the first). |
| `error` | The last rejection, or `null`. |
| `loading` | `true` until the first successful result (no `data` yet). |
| `fetching` | `true` during **any** in-flight fetch, including a refetch over existing data. |
| `refetch()` | Re-run the fetcher. A newer call supersedes an older in-flight one. |
| `mutate(next)` | Set `data` directly without fetching (optimistic update). Value or `(prev) => next`. |
| `stop()` | Stop the `refetchInterval` poller, if any. |
## Options
```js
query('feed', fetchFeed, {
initialData: [], // seed data; skips the initial `loading` state
refetchInterval: 30000, // poll every 30s
lazy: true, // with initialData: wait for the first refetch()
});
```
## Pairs with `derived`
Shape a query into exactly what a component needs, memoized — the view updates
as the request settles:
```js
import { query } from 'spark-html-query';
import { derived } from 'spark-html';
query('todos', fetchTodos);
derived('todoStats', ['todos'], (q) => ({
total: q.data?.length ?? 0,
done: q.data?.filter((t) => t.done).length ?? 0,
loading: q.loading,
}));
```
> `loading` vs `fetching`: show a **skeleton** on `loading` (first load, no data
> yet) and a subtle **spinner** on `fetching` (background refresh that keeps the
> stale data visible). That's the stale-while-revalidate pattern, declaratively.