Pages
Pages are React components that render custom UI views for a deployed agent. They live in pages/ and use SDK hooks to read from stores, invoke skills, and display structured data. This is how you build dashboards, morning briefs, investigation views, and other composed screens.
pages/
├── ops-dashboard.tsx
├── morning-brief.tsx
└── deal-detail.tsxPage Format
Each page file exports a page config object and a default React component:
export const page = {
name: "ops-dashboard",
description: "Facility overview — alerts and device status",
stores: ["active-alerts", "zone-status"],
automations: ["scan-alerts"],
};
export default function OpsDashboard() {
const [alerts, setAlerts] = React.useState([]);
React.useEffect(() => {
fetch("/api/stores/active-alerts")
.then((res) => res.json())
.then((data) => setAlerts(data.documents || []))
.catch(() => {});
}, []);
return (
<div
style={{
padding: "24px",
background: "#0f1117",
minHeight: "100%",
color: "#e4e4e7",
}}
>
<h1>Operations Dashboard</h1>
<p>{alerts.length} active alerts</p>
</div>
);
}Page Config
| Field | Type | Default | Description |
|---|---|---|---|
name | string | filename | Page identifier and URL slug |
description | string | — | Shown in the page header and sidebar tooltip |
stores | string[] | — | Stores this page reads from. Shown in the data source bar with links. |
automations | string[] | — | Automations that populate the page data. Shown in the data source bar with status, toggle, and run button. |
icon | string | — | Lucide icon name (e.g., 'shield', 'monitor', 'bar-chart') |
hidden | boolean | false | If true, excluded from sidebar |
When stores or automations are declared, the runtime automatically shows a data source bar above the page with store links, automation live/paused toggle, schedule, and a run button. This is a platform feature — no code required in the page itself.
SDK Hooks
Pages use hooks from @amodalai/react to access agent data:
| Hook | Description |
|---|---|
useStoreList(store, options) | Fetch multiple documents with filtering, sorting, and pagination |
useStore(store, key) | Fetch a single document by key |
useSkillAction(skill, options) | Invoke a skill from the page |
useStoreList
const { data, loading, error, refresh } = useStoreList("classified-alerts", {
filter: { severity: "P1" },
sort: { field: "timestamp", order: "desc" },
limit: 50,
refreshInterval: 10000, // auto-refresh every 10s
});useStore
const { data: alert } = useStore("classified-alerts", alertId);useSkillAction
const { invoke, loading } = useSkillAction("triage");
const handleTriage = () => {
invoke({ query: "Triage the latest alerts" });
};Sidebar Integration
Pages appear in the agent UI under a Pages section. The name is auto-formatted from the filename (ops-dashboard → "Ops Dashboard"). Click a page to navigate to /pages/{pageName}.
Hidden pages (hidden: true) are excluded from the sidebar but still accessible via direct URL — useful for detail pages navigated to from other pages.
Context Pages
Pages with context params are detail views that receive route parameters:
export const page = {
name: "deal-detail",
icon: "file-text",
context: { dealId: "string" },
hidden: true, // navigated to, not shown in sidebar
};
export default function DealDetail({ dealId }: { dealId: string }) {
const { data: deal } = useStore("deals", dealId);
// ...
}Entity Pages vs. Composed Pages
- Entity pages are auto-generated from store definitions — list and detail views come for free without writing page files
- Composed pages are what you define in
pages/— custom views that combine data from multiple stores with custom layout and logic
Only composed pages need explicit page files. If you just need a list/detail view of a single store, the runtime generates that automatically.
Deploying Page Changes
In Studio, page edits follow the normal source workflow: edit, review the diff, commit, deploy, and promote when ready. This keeps dashboard changes tied to the same deploy history as skills, knowledge, connections, stores, and automations.
Example: Surveillance Dashboard
import { useStoreList } from "@amodalai/react";
export const page = {
name: "ops-dashboard",
icon: "shield",
description: "Facility surveillance overview",
};
export default function OpsDashboard() {
const { data: alerts } = useStoreList("classified-alerts", {
sort: { field: "confidence", order: "desc" },
limit: 10,
});
const { data: zones } = useStoreList("zone-status");
const { data: devices } = useStoreList("device-profiles");
return (
<div className="grid grid-cols-2 gap-4">
<section>
<h2>Active Alerts</h2>
{alerts?.map((a) => (
<AlertCard key={a.key} alert={a.payload} />
))}
</section>
<section>
<h2>Zone Status</h2>
{zones?.map((z) => (
<ZoneCard key={z.key} zone={z.payload} />
))}
</section>
</div>
);
}