Fresh UI & design
NetScript gives you a front end with the same contracts-first rigor as the back end — and it
does so in two distinct layers that are easy to conflate. There is @netscript/fresh, a real
meta-framework (a published package of Fresh runtime extensions, route/island builders, a
TanStack-Query bridge, forms, and a streams client), and there is the scaffolded dashboard app
(apps/dashboard) that the meta-framework powers. The package is the reusable engine; the app is
one fully-wired showcase built on top of it. Both ship in every workspace, but they live at
different addresses and you reach for them at different moments.
Two layers, one design grammar
| Name | Type | Description |
|---|---|---|
@netscript/fresh |
meta-framework (package) |
A published Deno package: Fresh runtime extensions, the definePage/island builders, a TanStack-Query bridge, forms, defer primitives, a streams client, and route contracts. Reusable across any Fresh app you build. Reference: /reference/fresh/. |
apps/dashboard |
scaffolded app |
One concrete Fresh 2.x application generated into your repo, built ON @netscript/fresh and wired to your users service. The worked showcase you copy patterns from — not a finished product UI. |
@netscript/fresh-ui |
component registry |
The copy-source component library + design tokens installed into apps/dashboard/components/ui/ via netscript ui:init / ui:add. You own the copied files. Reference: /reference/fresh-ui/. |
The meta-framework: @netscript/fresh
@netscript/fresh is a published package (Fresh runtime extensions, builders, forms, defer primitives, and route contracts for NetScript) built on @fresh/core 2.x + Preact. You consume it
through targeted subpath exports rather than one fat barrel — import only the surface you need.
The full generated API for every subpath lives at the
@netscript/fresh reference; the table below is the map.
| Name | Type | Description |
|---|---|---|
@netscript/fresh/server |
bootstrap |
defineFreshApp |
@netscript/fresh/builders |
page builder |
The definePage() / route-reference builders that bind a typed route, declare server-loaded layers, and wire telemetry. |
@netscript/fresh/route |
route contracts |
Route-reference primitives (createRouteReference) for typed, generated route patterns consumed via router.ts. |
@netscript/fresh/query |
TanStack bridge |
QueryIsland, useQuery, useMutation, useQueryClient — the TanStack-Query hydration bridge over your contract-derived query factories. |
@netscript/fresh/form |
forms |
Typed form primitives for server-validated, contract-aware form handling. |
@netscript/fresh/defer |
streaming defer |
Defer primitives for streaming/deferred server rendering of slower layers. |
@netscript/fresh/streams |
streams client |
The Fresh-side durable-stream client for consuming HTTP/SSE durable streams in the browser (the producer runtime lives in @netscript/plugin-streams-core). |
@netscript/fresh/interactive |
interactivity |
Interactive island runtime helpers used by the hydrated client islands. |
@netscript/fresh/vite |
build |
The Vite integration the dev/build pipeline uses. |
@netscript/fresh/testing |
testing |
Test helpers for exercising pages, islands, and route contracts. |
@netscript/fresh/error |
diagnostics |
Typed error/diagnostics surface for the Fresh runtime. |
The scaffolded app: apps/dashboard
Every NetScript workspace ships apps/dashboard — a Fresh 2.x
application built with Preact, Tailwind CSS v4, and Vite. It is a workspace member registered in the
root deno.json and orchestrated by Aspire alongside your services. It boots through
defineFreshApp from @netscript/fresh/server, the meta-framework's baseline bootstrap, so every
NetScript Fresh app starts the same way. It is not a placeholder: the scaffold wires it directly
to your oRPC contracts so the dashboard renders typed, server-prefetched data from the same users
service the back end implements.
| Name | Type | Description |
|---|---|---|
main.ts |
entry |
App entry: export const app = defineFreshApp |
router.ts |
routing |
Stable route entrypoint. Re-exports generated routePatterns + routes and builds typed appRoutes via createRouteReference. |
routes/ |
pages |
File-system routes: index.tsx, dashboard.tsx, health.tsx, examples/service/, examples/telemetry/, the (design) system pages, plus _app.tsx / _layout.tsx shells. |
islands/ |
interactivity |
Client-hydrated Preact islands (e.g. ThemeToggle, SidebarToggle, Toast under islands/ui/). |
components/ui/ |
design system |
The copy-source component library you own (@netscript/fresh-ui): button, card, data-table, form-field, badge, and more (tsx + matching CSS in assets/ui/). |
lib/ |
service wiring |
example-service.ts builds a typed oRPC client + query factories from your contract; cn.ts, public-types.ts. |
assets/ |
styling |
design.css, tokens.css/json, theme-bridge.css, and per-component CSS — the Tailwind v4 + design-token layer. |
.generated/ |
generated |
manifest.ts + routes.ts produced by the Fresh route generator; consume via router.ts, never directly. |
Contract-driven by default
The dashboard does not hand-roll fetch calls. apps/dashboard/lib/example-service.ts imports your
UsersContractV1 and turns it into a typed client and TanStack-Query factories through the NetScript
SDK — the same contract object the users service implements, so the UI cannot drift from the API.
// apps/dashboard/lib/example-service.ts
import { createServiceClient } from '@netscript/sdk/client';
import { createQueryFactories } from '@netscript/sdk/query';
import { bridgeInvalidation } from '@netscript/sdk/query-client';
import { UsersContractV1 } from '@plugin-smoke/contracts';
export const exampleServiceName = 'users';
export const exampleServiceClient = createServiceClient<typeof UsersContractV1>({
contract: UsersContractV1,
serviceName: exampleServiceName,
routerName: 'users',
});
// queryOptions / mutationOptions derived straight from the contract.
export const exampleServiceQueries = createQueryFactories({
service: { contract: UsersContractV1, client: exampleServiceClient },
}).service;
A route is declared with the meta-framework's definePage() builder (@netscript/fresh/builders)
— it binds a typed route reference, declares server-loaded layers, and wires telemetry — while an
island consumes the query factories above through the @netscript/fresh/query bridge for
hydration, optimistic mutations, and cache invalidation on the client.
// definePage() declares a typed page: route + server-loaded layers + telemetry.
import { appRoutes } from '@app/router.ts';
import { definePage } from '@app/utils.ts';
import { ServiceExampleLabPanel } from './(_components)/lab-panel.tsx';
import { loadServiceShowcaseData } from './(_shared)/service-showcase.ts';
export const serviceExamplePage = definePage()
.withRoute(appRoutes.serviceExample)
.withPolicy('balanced')
.withTelemetry({ enabled: true, spanName: 'scaffold.examples.users' })
.withMeta(() => ({ title: 'users example', description: 'Backed by the users service.' }))
.withLayer('lab', ServiceExampleLabPanel, { loader: loadServiceShowcaseData })
.build();
export const { default: page } = serviceExamplePage;
export { page as default };
// Client-hydrated island: typed query + optimistic mutation over the contract.
import { QueryIsland, useMutation, useQuery, useQueryClient } from '@netscript/fresh/query';
import { exampleServiceQueries, exampleServiceListInvalidation } from '@app/lib/example-service.ts';
const Lab = (props: { input: { status?: string } }) => {
const queryClient = useQueryClient();
const { data, refetch, isRefetching } = useQuery({
...exampleServiceQueries.list.queryOptions(props.input),
staleTime: 15_000,
});
const advance = useMutation({
...exampleServiceQueries.updateStatus.mutationOptions(),
onSettled: () => queryClient.invalidateQueries(exampleServiceListInvalidation),
});
return <button type='button' onClick={() => void refetch()}>{isRefetching ? '…' : 'Refresh'}</button>;
};
export default (props: { input: { status?: string } }) => (
<QueryIsland><Lab {...props} /></QueryIsland>
);
The design system & owned components
The scaffold installs the NetScript Fresh UI foundation (@netscript/fresh-ui) into
apps/dashboard/components/ui/ and its CSS into assets/ui/, driven by design tokens in
assets/tokens.json / tokens.css. Because the components are copied into your repo, editing the
UI is editing your own files — there is no framework component you cannot open. The bundled
(design) route group renders a live token, component, and composition gallery so you can see every
primitive in your project.
You manage that library with two CLI commands (run from the workspace root):
| Name | Type | Description |
|---|---|---|
netscript ui:init |
install foundation |
Installs the Fresh UI foundation set into an app workspace. The scaffold runs the equivalent for you; run it once when adding UI to an app that lacks it. |
netscript ui:add |
add an item |
Copies one registry item or a named collection into components/ui/, wires its CSS, and merges any required deno.json imports. Example: netscript ui:add data-table. |
Scope
The scaffolded dashboard is a working, contract-wired showcase — not a finished product UI. It
ships the users example (server-prefetched list + optimistic status mutation), a telemetry
example, a CRUD example, a health page, and the design gallery. Treat these as the canonical patterns
to copy from, not features to ship as-is; build your real screens by following the same
definePage + island + query-factory shape against @netscript/fresh. The meta-framework is the
durable surface — the app is one application of it.
Endpoints & ports
| Name | Type | Description |
|---|---|---|
:8010 |
port |
Dashboard dev server (Deno.env.get('PORT') || '8010'). Standalone: deno task --cwd apps/dashboard dev. |
/health |
HTTP |
App health route logged at startup (http://localhost:8010/health). |
:18888 |
Aspire |
Aspire dashboard that orchestrates the dashboard alongside services/plugins; token printed by aspire start. |
:3001 |
upstream |
The users service the dashboard's typed client calls — same UsersContractV1, no drift. |
:4437 |
streams |
The durable-streams Aspire service the @netscript/fresh/streams client consumes over HTTP/SSE (producer runtime in @netscript/plugin-streams-core). |
Where to go next
This hub is intentionally thin — the full generated APIs live in the reference. Pick the lane that matches what you're doing.