Daslab scenes as React hooks
A scene holds assets; tools act on them; hooks watch them. That one sentenceis the whole SDK. A scene can hold anything — a Gmail account, a Postgres
database, a GitHub repo, a spreadsheet, a robot, a parts catalog — and every
tool of every connected provider becomes a typed React hook. One import, every
integration.
Your first page needs no account and no API key — keyless providers answer
anonymous calls, and public scenes serve their assets to anyone. (The example
below happens to hit the parts catalog because it's keyless; once you
bring a token, the very same hook calls any provider your scene has.)
import { DaslabProvider, useDaslabCall } from "@daslab/sdk/react";
function Bom() {
const { data, loading } = useDaslabCall("parts_configure", {
use_case: "machine-monitoring",
scale: 12,
market: "th",
});
if (loading) return <p>configuring…</p>;
return (
<h1>
{data.total_display} <small>{data.per_unit_display}</small>
<a href={data.share_url}>open configurator ↗</a>
</h1>
);
}
export default function Page() {
return (
<DaslabProvider>
<Bom />
</DaslabProvider>
);
}
Paste it, npm run dev, and you have live data on your page in sixty seconds
with zero keys. Add a token and the same pattern reads your inbox, queries your
database, files your issues — nothing about the code changes but the tool name.
One vocabulary, two runtimes
The hook names you use in the browser are the hook names scenes use on the
server. A reactive scene definition and your React page speak the same
language:
// In a scene definition (runs on Daslab):
const mail = await gmail.useInbox(ctx, { query: "is:unread" });
// In your React app (runs in the browser):
const d = useDaslab();
const mail = d.gmail().useInbox({ query: "is:unread" });
Learn the vocabulary once; write scenes, dashboards, public sites, or agent
harnesses with it.
The surface
Five hooks, two components. That's the whole thing.
| Hook | What it gives you |
|---|---|
useScene(ref) | A public scene's identity + assets, as live data. Ref is a handle (acme/showroom), slug, or id. |
useAsset(ref) | One asset from that scene — fields carries a note's body, a widget's data. |
useDaslabCall(tool, input) | Any provider tool, SWR-style: cache, dedup, refetch, enabled. MCP text results are parsed to JSON for you. |
useDaslabChat(opts) | The full conversation surface — streaming, resume, recovery. Every chat flow is "follow the job". |
useDaslab() | The raw typed client — every generated provider hook hangs off it. |
| Component | What it renders |
|---|---|
<Chat/> | The Daslab chat surface, themable via chat.css. |
<Widget data={…}/> | Any widget payload — metric, table, list, record, key-value — as a presentable block. Hooks return the data; <Widget/> renders it — the view half of a cell, so you never start from a blank div. |
import { useScene, useAsset, Widget } from "@daslab/sdk/react";
function Showroom() {
const { scene, assets, loading } = useScene("acme/showroom");
if (loading) return null;
return (
<main>
<h1>{scene.name}</h1>
{assets.map(a => <Widget key={a.id} data={a.fields?.data ?? a.fields} />)}
</main>
);
}
Every provider, one grammar
Every tool of every provider your scene has an account for is a typed hook on
the client — Gmail, Sheets, GitHub, Postgres, Stripe, Slack, S3, SAP, search,
robots, a hundred-plus more. The grammar never changes:
function OpsDashboard() {
const d = useDaslab();
const leads = d.sheets("Lead-Tracker").useRead({ maxRows: 200 });
const mention = d.brave().useWebSearch({ query: "daslab.run" });
const scrape = d.firecrawl().useScrape({ url: "https://competitor.example" });
// …every d.<provider>(name).use<Verb>(input) is a hook; render as you like
}
Property access is not a hook — only the final use* call is, so the Rules of
Hooks hold. Autocomplete is the reference: the input and output types are
generated from the same schemas the server enforces.
Public by design
- Anonymous tool calls run against keyless providers only (Parts Catalog,
the answer is the same for everyone. Everything credentialed requires your
Bearer token, exactly as before.
- Public scenes opt in explicitly: a scene declares a public role in its
useScene sees only the assets that role's layers expose.
Private by default, layer-scoped when public.
<DaslabProvider> {/* public mode — no token */}
<DaslabProvider token={key} scene={id}> {/* your scenes, your tools */}
The escalation ladder
- Read a public scene —
useScene("acme/showroom"), no key. - Call a keyless tool — the BOM example above, no key.
- Bring a token — your scenes, every provider you've connected, same hooks.
- Let the agent write it — this SDK ships
llms.txt; paste it into your
Honest changelog
Named, designed, not yet shipped — watch this page:
useScene(ref, { at })— scenes are versioned like git; time-travel reads
useSceneHistory(ref)— the commit list behind{ at }.useJob(id)— headless job following. TodayuseDaslabChatfollows jobs and
- React hooks section in the generated
llms.txt.
Install
npm install @daslab/sdk
import { DaslabProvider, useScene, useAsset, useDaslabCall, Widget } from "@daslab/sdk/react";
Server-side (Node/Bun) usage of the same client is documented in the package README and llms.txt; the CLI/MCP surface is at /docs/cli.
Updated 2026-07-23