@k8ordo/framework

Options and files

The options of framework(), the files src/routes/ may hold, and what each entry point of @k8ordo/framework exports. How to use each is in the guide pages.

On this page

Entry points

@k8ordo/framework splits its entry points by where the code runs. No entry point that a deployed application loads imports Vite.

  • @k8ordo/framework: the types and functions the application’s code imports.
  • @k8ordo/framework/vite: framework(), the plugin for vite.config.ts. It loads Vite.
  • @k8ordo/framework/server: server mode only. The functions and types that code inside the handler imports. In static mode, a module that imports a value from it fails the build. It needs nothing from Node.js but AsyncLocalStorage, so it runs wherever the handler does.
  • @k8ordo/framework/serve: serve(), which runs a build on Node.js. A server mode build’s dist/server.js calls it, and a server of your own can too. A static mode build it serves the way a static host does.
  • @k8ordo/framework/vercel: vercel(), the plugin that writes the build for Vercel, in either mode.
  • @k8ordo/framework/generated: what the generated .k8ordo/ files import. Code written by hand does not import it.

framework

Import from @k8ordo/framework/vite

The Vite plugin that builds the application. It returns an array holding React’s plugin and the RSC pipeline, so pass it to plugins as it is.

ts
framework(options: FrameworkOptions): PluginOption[]

Parameters

options.mode'static' | 'server'
'static' or 'server', with no default. 'static' renders pages at build time, into an app that runs with no server. 'server' renders pages per request, in an app that runs on a server.
options.routesDirstring
The route directory, relative to the project root. src/routes by default. The generated files go to .k8ordo/ either way.
options.paths(patterns: readonly string[]) => readonly string[] | Promise<readonly string[]>
Static mode only. A function that returns the pathnames of routes with parameters. It receives the patterns that need values and returns an array of pathnames, or a promise of one. The pathnames carry no Vite base. For a page with a fallback.tsx, it may also return shell locations that leave a value open, such as /ja/posts/:id.
options.sitestring
Static mode only. The origin the site is served from, such as https://example.com. With it, the build also writes sitemap.xml. It is also the origin of the request.url a route.ts receives.
options.cspReadonly<Record<string, readonly string[]>>
Static mode only. The Content-Security-Policy every page carries, as an array of sources per directive. The build adds the hashes of the framework’s scripts and writes it into a <meta>.

Returns

PluginOption[] — An array of Vite plugins.

Caveats

  • paths, site or csp written beside mode: 'server' is a type error.
  • When csp holds a directive a <meta> ignores, or 'strict-dynamic', framework() fails as soon as it is called, with the error the "csp" option cannot go into a page's <meta> as it is.
vite.config.ts
import { framework } from '@k8ordo/framework/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    framework({
      mode: 'static',
      paths: () => ['/products/1', '/products/2'],
      site: 'https://example.com',
    }),
  ],
});

Route files

The files a directory under src/routes/ may hold, with what each exports and receives. Any other file lives outside src/routes/: components in src/components/, every other module in src/lib/.

page.tsx

  • Default export: the page component for the directory’s URL. It receives params and pathname.
  • In server mode, it also receives request, a RouteRequest.
  • paramsSchema: a schema that validates the parameters. Anything that implements Standard Schema works. Optional.
  • search: server mode only. The schema of the query the page reads, the url of a @k8ordo/state definition. A page that exports it receives the parsed values as search.

layout.tsx

  • Default export: a component that wraps what renders below it, received as children. It also receives params and pathname. params stays typed as strings.
  • In server mode, it also receives request, a RouteRequest.
  • paramsSchema: runs for every page below, before the page’s own schema.

not-found.tsx

  • Default export: the component rendered for a URL below the directory that no route matched. It receives params and pathname. The params are unvalidated strings.
  • In server mode, it also receives request, a RouteRequest.
  • In static mode, it is written as 404.html, so an application may hold only one. In server mode, it answers with a 404 status, and each directory may hold its own.

error.tsx

  • A 'use client' file.
  • Default export: a component that receives error and reset. It renders in place of what is below it when that throws. It receives no params.

loading.tsx

  • Default export: a component with no props. It renders as the <Suspense> fallback while the page below it loads.

fallback.tsx

  • Default export: a component with no props. Under static mode it renders in place of the page.tsx beside it, for a URL whose value paths did not list.
  • It sits beside a page.tsx that has a parameter. Its directory may not hold a layout.tsx.
  • Server mode does not use it: page.tsx renders every value per request.

redirect.ts

  • Default export: the target as a string, or { to, permanent }. to is a pattern of the route table. Its parameters are filled with the matched values.
  • In static mode, it is written as a page that sends the visitor to the target. In server mode, it answers GET and HEAD with a 307, or a 308 when permanent.

route.ts

  • Exports named after methods, such as GET and POST: functions that receive { request, params } and return a Response.
  • paramsSchema: validates the parameters, as a page’s does.
  • In static mode, only GET may be exported, and its response is written as the file at that URL. In server mode, any of the seven methods may be exported.

guard.ts

  • Server mode only. Default export: a function of type Guard. It runs before anything below the directory responds. Returning a Response answers the request there. Returning nothing goes on to the page or the route.ts.

Application API

ts
import {
  bindParams,
  href,
  isNotFound,
  matchPath,
  navigateTo,
  normalizePathname,
  notFound,
  useMatch,
  usePathname,
  usePendingPathname,
  withBase,
} from '@k8ordo/framework';
import type {
  BoundLinks,
  BoundParams,
  ErrorProps,
  LayoutProps,
  MatchablePattern,
  MatchOptions,
  NavigateToOptions,
  PageProps,
  RegisteredNavigablePattern,
  RegisteredPageParams,
  RegisteredParams,
  RegisteredPattern,
  RouteContext,
} from '@k8ordo/framework';

Each is an export of @k8ordo/router, re-exported by name. useParams and useRoute are not among them: they read a <Router> the framework never renders. A page receives params as a prop instead. Each one is described on the API page of @k8ordo/router.

cookies

Import from @k8ordo/framework/server

Server mode only. Reads and writes the request’s cookies. Call it from a guard.ts, a route.ts or a Server Action.

ts
cookies(): Cookies

Fields

get(name: string) => string | undefined
Reads a value by name. undefined when there is none.
has(name: string) => boolean
Whether a cookie of that name exists.
set(name: string, value: string, options?: CookieOptions) => void
Writes a value. The third argument takes the attributes of CookieOptions.
delete(name: string, scope?: CookieScope) => void
Deletes a cookie. Pass the path and domain it was written with.

Caveats

  • A read reflects what was written earlier in the same request. A name not written yet returns the value the request carried.
  • Every write goes to the browser as a Set-Cookie on the response, whatever the response is.
  • Called while a page renders, it fails with cookies() belongs to what answers the request. A page reads request.cookies instead.
ts
cookies().get('session');
cookies().set('session', token, { maxAge: 60 * 60 * 24 });
cookies().delete('session');

Import from @k8ordo/framework/server

The cookie attributes passed to cookies().set() as its third argument. The defaults suit a session cookie.

ts
type CookieOptions = {
  readonly path?: string;
  readonly domain?: string;
  readonly maxAge?: number;
  readonly expires?: Date;
  readonly httpOnly?: boolean;
  readonly secure?: boolean;
  readonly sameSite?: 'strict' | 'lax' | 'none';
};

Fields

pathstring
/ by default.
domainstring
Not set by default.
maxAgenumber
The lifetime in seconds. 0 expires it at once.
expiresDate
The expiry, as a Date.
httpOnlyboolean
true by default, so a page’s script cannot read it.
secureboolean
true by default, so it is sent over HTTPS only. For an HTTP request to localhost, 127.0.0.1 or [::1], the default is false.
sameSite'strict' | 'lax' | 'none'
'lax' by default. 'none' is always secure, by default too. Passing it with secure: false is an error.

responseHeaders

Import from @k8ordo/framework/server

Server mode only. Returns the Headers the final response will carry. Call it from a guard.ts, a route.ts or a Server Action.

ts
responseHeaders(): Headers

Caveats

  • A header set here goes on the response, whether that is a page’s HTML, its RSC payload or a Response a guard.ts returned. A header the response already carries is replaced.
  • Outside a request it fails with responseHeaders() needs a request, and inside a page render with responseHeaders() belongs to what answers the request.

requestHeaders

Import from @k8ordo/framework/server

Server mode only. Returns the headers the request arrived with. Call it from a guard.ts, a route.ts or a Server Action. It is how a Server Action, which receives only its arguments, reads the headers.

ts
requestHeaders(): Headers

Caveats

  • Outside a request it fails with requestHeaders() needs a request, and inside a page render with requestHeaders() belongs to what answers the request.

redirect

Import from @k8ordo/framework/server

Server mode only. Ends a Server Action and redirects to another URL.

ts
redirect(to: string): never

Parameters

tostring
The target URL. It is sent as written, so build it with href().

Returns

never — Never returns. The lines after it never run.

Caveats

  • It ends the action by throwing, so do not call it inside a try.
  • A form posted before JavaScript loaded gets a 303. A call from the client runtime gets a payload that tells the router where to go.

nonce

Import from @k8ordo/framework/server

Server mode only. Returns the nonce this response’s inline scripts carry. It is new for every request.

ts
nonce(): string

Caveats

  • While the request is being handled, the render included, it returns the same value.
  • Outside a request it fails with nonce() needs a request.

Guard

Import from @k8ordo/framework/server

Server mode only. The type of what a guard.ts default-exports. Pass its directory’s pattern as the type argument.

ts
type Guard<P extends string = string> = (
  context: GuardContext<P>,
) => Response | void | Promise<Response | void>;

Fields

requestRequest
The Request as it arrived.
paramsParamsOf<P>
The parameters of the directory’s pattern. A guard.ts runs before any schema, so they are the URL’s strings.

RouteRequest

Import from @k8ordo/framework/server

Server mode only. The type of the request a page, a layout and a not-found.tsx receive. Both fields are read-only.

ts
type RouteRequest = {
  readonly headers: Headers;
  readonly cookies: ReadonlyMap<string, string>;
};

Fields

headersHeaders
The request’s headers.
cookiesReadonlyMap<string, string>
The Cookie header parsed by name. When a name appears twice, the first value is used.

RedirectTarget

Import from @k8ordo/framework/server

The type of what a redirect.ts default-exports. Write satisfies RedirectTarget to check the shape where it is written. It is a type-only import, so static mode may use it too.

ts
type RedirectTarget =
  | string
  | { to: string; permanent?: boolean };

serve

Import from @k8ordo/framework/serve

Runs the build on a Node.js HTTP server. Once it is listening, it returns its URL and a function to stop it.

ts
serve(options?: ServeOptions): Promise<Server>

Parameters

options.diststring
The build output directory. dist by default, resolved from the working directory.
options.portnumber
The port to listen on. 3000 by default. 0 lets the system pick a free one.
options.hoststring
The host to listen on. The default, localhost, is reachable from the same machine only, so pass 0.0.0.0 inside a container.

Returns

Promise<Server> — The server, once it is listening.

Fields

portnumber
The port it actually listens on.
urlstring
Its URL, in the form http://<host>:<port>.
close() => Promise<void>
Stops the server. The promise resolves once it has stopped.

Caveats

  • To call it from a server of your own, put @k8ordo/framework in dependencies and install it for run time as well. The dist/server.js a server mode build writes has it bundled in.
  • For a server mode build, a GET or HEAD that names a file in dist/client/ gets that file. Everything else goes to the handler.
  • When the handler throws, it responds with a 500 and the body internal error and logs what was thrown.
  • For a static mode build, the handler is never called. It looks for the file, then a rewrite in _redirects, then 404.html, and answers any method but GET and HEAD with a 405.
serve.js
import { serve } from '@k8ordo/framework/serve';

const server = await serve({ port: 3000 });
console.log(server.url);

vercel

Import from @k8ordo/framework/vercel

A plugin that has vite build also write .vercel/output/ in the shape of Vercel’s Build Output API (v3). Put it beside framework(), in either mode.

ts
vercel(): Plugin

Returns

Plugin — A Vite plugin.

Caveats

  • Under server mode the function is the handler, with every dependency bundled in. A dependency left out with resolve.external is not in the function, and does not work on Vercel.
  • Under static mode there is no function: only the written files, and routes that rewrite a value the build did not write to its shell. Vercel does not read _redirects.
vite.config.ts
import { framework } from '@k8ordo/framework/vite';
import { vercel } from '@k8ordo/framework/vercel';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [framework({ mode: 'server' }), vercel()],
});
k8ordo

React libraries that use Baseline features without holding back.

© 2026 k8o — MIT License

Typeset in Noto Sans JP & M PLUS 2