Skip to main content
Alpha

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

The meta-framework vs. the scaffolded app
NameTypeDescription
@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.

@netscript/fresh subpath exports (the meta-framework surface)
NameTypeDescription
@netscript/fresh/server bootstrap defineFreshApp(...) — the baseline app bootstrap (static files, file-system routes) so every NetScript Fresh app boots identically.
@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.

apps/dashboard layout (verbatim from a fresh scaffold)
NameTypeDescription
main.ts entry App entry: export const app = defineFreshApp({ name: 'dashboard' }). Reads PORT (default 8010) and logs a startup banner.
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):

Fresh UI CLI commands
NameTypeDescription
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

Fresh UI runtime surface
NameTypeDescription
: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.