GioJSdocs
On this page

Page Exports

Every module export GioJS reads from the files of an app - pages, layouts, route handlers, special files and config - and what each one does.

GioJS configures a route with what its files export, not with a config object. The tables below list every export the framework reads, by file. An export that is not listed is ignored: it is an ordinary export of your module and nothing more.

app/posts/[id]/page.tsx
export const revalidate = 3600;                       // cache the render for an hour
export const tags = ['posts'];                         // purge it with revalidateTag('posts')

export async function getServerSideProps(ctx) { /* load the post */ }
export async function generateMetadata(ctx, { props }) { /* its <title> */ }
export async function action(req) { /* handle the comment form's POST */ }

export default function PostPage({ post }) { /* render it */ }

Pages

app/**/page.tsx (or .jsx, .js):

ExportTypeWhat it does
defaultReact componentThe page. Receives what getServerSideProps returned, or { params, searchParams } without it, plus actionData after an action. See page.tsx.
getServerSidePropsasync functionLoads data on the server for each render; may redirect, answer 404 or set headers.
actionasync functionHandles a POST to the page's URL: redirect, re-render with actionData, or a Response.
metadataMetadataStatic head metadata: title, description, Open Graph, canonical URL, ...
generateMetadatafunctionHead metadata per request, from params, query or the page's props.
revalidatenumber | falseCaches the render in the Rust server for that many seconds (false: until a purge or deploy). Not set: rendered per request.
tagsstring[]Cache tags for revalidateTag().
shell'cache'Partial prerendering: caches the part before the Suspense boundaries, streams the holes per visitor.
getStaticPathsfunctionThe params of a dynamic page to write under gio export. The server ignores it.
GETfunctionLegacy: a page with no default export can answer with Server-Sent Events.
dynamicstringTyped, but has no effect.

Layouts

app/**/layout.tsx (or .jsx, .js):

ExportTypeWhat it does
defaultReact componentWraps every page below its folder; receives { children, path }. The root layout renders <html> and is never hydrated. See layout.tsx.
metadataMetadataHead metadata, merged under the pages below (title templates, a site-wide openGraph, metadataBase).
generateMetadatafunctionHead metadata per request; props is undefined in a layout.

revalidate, tags, shell and getServerSideProps are read from pages only: in a layout they have no effect.

Route handlers

app/**/route.ts (or .js):

ExportTypeWhat it does
GET, POST, PUT, PATCH, DELETERouteHandlerAnswer that HTTP method with a Response, JSON, null (204) or a GioEventStream. HEAD runs GET.
wsHandlerWsHandlerAccepts WebSocket connections at the same path.

Route handler responses are never cached, so revalidate has no effect in a route.ts. See route.ts.

Special files

FileExportsWhat they do
error.tsxdefault, metadata, generateMetadataThe error page of its folder: a client error boundary that receives { error, reset }.
not-found.tsxdefault, metadata, generateMetadataThe 404 page of its folder; the component gets no props.
loading.tsxdefaultThe Suspense fallback around its folder.
app/sitemap.ts, app/robots.ts, app/manifest.tsdefault, revalidateThe data of /sitemap.xml, /robots.txt and /manifest.webmanifest: a value, or a function returning (a promise of) one. Cached for revalidate seconds, 3600 by default.

Project files

FileExportWhat it does
middleware.tsdefaultdefineMiddleware({ redirects, rewrites, headers, guards }): request rules the Rust server applies before rendering.
gio.config.tsdefaultdefineConfig({ plugins }): Node plugins. Unknown keys stop startup.

Where exports run

Everything on this page runs on the server. The browser bundle of a route imports only the default exports of its page, its nested layouts and its error.tsx and loading.tsx files, so the other exports - and the modules only they import, such as a database client - are left out of it. The root layout is never sent to the browser at all. A helper built by a module-scope call (export const getServerSideProps = withAuth(...)) can keep its imports in the bundle: mark such modules server-only so a leak fails the build.

The legacy page GET export

Before route handlers existed, a page could stream Server-Sent Events. That still works, for compatibility: a page.tsx with no default export whose GET(req) returns a GioEventStream answers with the event stream.

app/legacy-feed/page.tsx
import { GioEventStream } from '@gio.js/core';

// No default export: GET answers the request.
export function GET() {
  return new GioEventStream((stream) => {
    stream.send({ hello: 'world' });
    stream.close();
    return () => {};                               // nothing to clean up
  });
}
  • A page that has a default export never calls its GET: the component renders.
  • Write new streams as a GET in a route.ts instead: it can also return any Response, a streamed body included, and it sits next to the other methods of the endpoint.

dynamic has no effect

The page module type declares dynamic?: 'force-dynamic' | 'force-static' | 'auto', but GioJS never reads it. Whether a page is cached is decided by revalidate alone: leave it out to render per request, export a number or false to cache. gio migrate rewrites a Next.js dynamic = 'force-static' page to revalidate = false. The same goes for the other Next.js segment options (runtime, fetchCache, dynamicParams, preferredRegion, maxDuration): GioJS ignores them.