Choosing a mode
The mode of framework() decides whether the application runs without a server or with one. The choice comes down to whether the application needs the request.
On this page
Choosing
Pick mode: 'server' if any of these applies.
- Receiving a form submission in a Server Action
- Stopping a request in a
guard.tsbefore the page renders - Reading the request’s headers or cookies, or writing cookies with
cookies() - Exporting
searchfrom a page to receive the query - Rendering pages on the server for values that cannot be listed ahead of time, such as the IDs of posts users create
- Responding to a method other than
GETfrom aroute.ts
If none of them applies, pick mode: 'static'. Pages are rendered at build time, so the site goes on a static host with no server. When the content changes, such as a new post, you build again. It suits a site whose content is the same for every visitor, such as documentation or a blog.
Static mode can still answer values that cannot be listed ahead of time. With a fallback.tsx beside a page.tsx, a value the build did not write has its body rendered in the browser. A page that does not need its body in the first HTML can stay in static mode. How to write one is covered in Parameters.
When in doubt, start with mode: 'static'. Anything you write later that needs the request fails the build or the type check.
Mode differences
- Build output: files in
dist/client/in static mode, a request handler in server mode. - A route with parameters: in static mode its values are listed in
paths, and afallback.tsxanswers the rest with a shell rendered in the browser. In server mode they arrive with the request, so new values need no rebuild. - A URL no route matches: in static mode the host serves
404.html, in server mode the application responds with a 404 status. redirect.ts: in static mode it is written as a page that sends the visitor on, in server mode it responds with307or308.route.ts: in static mode the response of itsGETis written as a file, in server mode it responds to every method it exports.- Content-Security-Policy: in static mode the
cspoption writes it into a<meta>, in server mode aguard.tswrites it as a header. - A page’s
request: not in the types in static mode, and reading it is a type error. Read without a type check, it fails the build. In server mode it holds the headers and cookies.
Switching
vite.config.tsimport { framework } from '@k8ordo/framework/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [framework({ mode: 'server' })],
});The files under src/routes/ and the imports from @k8ordo/framework work as they are in either mode. A fallback.tsx is simply unused under server mode, so there is no need to remove it.
paths, site and csp are options of static mode only. Written beside mode: 'server' they are a type error, and framework() fails when the config loads, so drop them when moving to server mode. Running a server mode build is covered in Deployment.
When moving to static mode, list the values of each route with parameters in paths, or give the page a fallback.tsx. Remove the refused code below too, and any read of a page’s request.
Refused code
vite buildstatic build cannot answer a request — a file is written once for every visitor, and these import @k8ordo/framework/server:
src/lib/session.ts
this application wants mode: 'server'A static mode build fails on the following code, which responds to a request. It lists the files it finds, one list per kind. A guard.ts, search and a route.ts's methods are checked before compiling; imports of @k8ordo/framework/server and 'use server' after it. Fixing the first can reveal the second on the next build.
- A module importing a value from
@k8ordo/framework/server - A
'use server'module - A
guard.ts - A page exporting
search - A
route.tsexporting a method other thanGET
Each error ends with the line this application wants mode: 'server'. vite dev reports the same error the moment it compiles the module. import type is erased before the build sees it, so it passes. A type such as RedirectTarget can be imported from @k8ordo/framework/server in static mode too. import { type RedirectTarget } is refused, though: under verbatimModuleSyntax it stays behind as an import of the module.
The build checks only the application’s own modules. A dependency in node_modules that imports @k8ordo/framework/server is not named; calling cookies() or the like from it fails instead. A route.ts or page that re-exports another module with export * fails too, since the build cannot read the names it exports.