page.tsx
The file that makes a folder under app/ a route. Its default export is the React component GioJS renders for that URL.
import React from 'react';
import type { PageProps } from '@gio.js/core';
export default function BlogPost({ params, searchParams }: PageProps<'/blog/:slug'>) {
return (
<article>
<h1>{params.slug}</h1>
{searchParams.preview === '1' && <p>Preview mode</p>}
</article>
);
}Reference
File name and location
A page file may sit in app/ or any folder below it, except inside a private folder. The folder path is the URL: app/page.tsx answers /, app/blog/[slug]/page.tsx answers /blog/:slug, and route groups add nothing to it.
| Extension | Precedence |
|---|---|
page.tsx | Used first |
page.jsx | Used when there is no page.tsx |
page.js | Used when there is neither |
A folder with several of them uses the first one and ignores the others, so a stray compiled page.js next to page.tsx never changes what renders. page.ts is not a page: pages contain JSX.
Props
A page without getServerSideProps renders with these props:
| Prop | Type | Default | Description |
|---|---|---|---|
params | Record<string, string> | - | The dynamic segments of the URL, by folder name: { slug: 'hello' }. A catch-all is one string with its / separators ('a/b'), an empty optional catch-all is '', and a static route gets {}. |
searchParams | Record<string, string> | - | The query string, decoded (+ becomes a space). One value per name: for ?tag=a&tag=b the last one wins. |
actionData | unknown | - | Only on the render that answers a POST to a page with an action: what the action returned. |
A page with getServerSideProps renders with exactly the props it returned, plus actionData after an action. Type them with InferPageProps<typeof getServerSideProps>; type the plain props with PageProps<'/blog/:slug'>. Both come from @gio.js/core, and the route pattern is checked against your routes (see .gio/).
Module exports
The default export is required. Everything else is optional:
| Export | What it does |
|---|---|
default | The page component. |
getServerSideProps | Loads the props on the server for every render; can redirect or answer 404. |
action | Handles POST requests to the page's URL (forms). |
metadata / generateMetadata | Title, description and the other <head> tags. |
revalidate | Caches the rendered page for that many seconds. |
tags | Cache tags for revalidateTag(). |
shell | 'cache' turns on partial prerendering. |
getStaticPaths | The params gio export writes for a dynamic route. |
All page exports are listed on Page Exports. getServerSideProps and getStaticPaths are removed from the browser bundle, together with the imports only they use.
Behavior
- Methods. A page answers
GETandHEAD, andPOSTwhen it exportsaction. Any other method gets405with a JSON body{"error":"Method Not Allowed"}and anAllowheader (GET, HEAD, plusPOSTwith an action, plus the methods of aroute.tsin the same folder). - Rendering. The page renders on the server inside its layouts, then hydrates in the browser from a per-route bundle. Its props travel in the HTML as JSON (
<script id="__gio_props">), so they must be JSON-serializable: a page whose props are not renders without hydration, with a warning in the log. - Status.
200withContent-Type: text/html; charset=utf-8.notFound()answers404with the nearest not-found.tsx, and a thrown error500with the nearest error.tsx. - Caching. Without
revalidateevery request renders and the response isCache-Control: private, no-cache. See Caching & Revalidating. - Navigation. On a soft navigation the page remounts whenever the path changes (
/posts/1to/posts/2), so its state starts fresh; a query-only change keeps it mounted.
Examples
Read params and the query string
import React from 'react';
import type { PageProps } from '@gio.js/core';
export default function Category({ params, searchParams }: PageProps<'/shop/:category'>) {
const sort = searchParams.sort ?? 'popular';
return (
<h1>
{params.category} - sorted by {sort}
</h1>
);
}/shop/shoes?sort=price renders "shoes - sorted by price".
Load data on the server
import React from 'react';
import { notFound } from '@gio.js/core';
import type { GetServerSideProps, InferPageProps } from '@gio.js/core';
import { getPost } from '../../../lib/posts';
export const getServerSideProps: GetServerSideProps<{ title: string; body: string }, '/posts/:id'> =
async (ctx) => {
const post = await getPost(ctx.params.id);
if (post === null) notFound();
return { props: { title: post.title, body: post.body } };
};
export default function Post({ title, body }: InferPageProps<typeof getServerSideProps>) {
return (
<article>
<h1>{title}</h1>
<p>{body}</p>
</article>
);
}A JavaScript page
/** @param {import('@gio.js/core').PageProps<'/about'>} props */
export default function About({ searchParams }) {
return <h1>About{searchParams.ref ? ` (from ${searchParams.ref})` : ''}</h1>;
}Good to know
- Only the
pagefile (and the other special names on File Conventions) is routed. Components, styles and tests can live next to it in the same folder without becoming URLs. - A page is not a React Server Component. It renders on the server and again in the browser, so it cannot be an
asyncfunction and cannot read files or secrets itself: load data ingetServerSideProps. - Unlike Next.js,
paramsandsearchParamsare plain objects, not promises, and a page withgetServerSidePropsdoes not receive them unless it returns them. - Param values are taken from the path as sent. GioJS decodes escapes of unreserved characters (
%41isA), but other escapes stay encoded:/blog/hello%20worldgives{ slug: 'hello%20world' }. CalldecodeURIComponent()when you need the text. - Two pages that answer the same URLs (for example in two route groups) stop startup with an error naming both files. A
route.tsin the same folder is allowed: it answers the methods it exports, and the page the rest. - Everything in the props ends up in the page's HTML. Never return secrets from
getServerSideProps.
Related
- Layouts & Pages - the guide.
- layout.tsx, route.ts, Dynamic Routes
- Page Exports - every export a page may have.
- Fetching Data and Forms & Mutations
Version history
| Version | Changes |
|---|---|
v0.1.0-beta.8 | Pages answer POST through an action export. Pages inside (group) folders no longer carry the group in their URL, pages in _private folders are never routed, and two pages answering the same URLs stop startup. PageProps and InferPageProps types. |
v0.1.0-beta.5 | Pages hydrate in the browser from per-route client bundles. |
v0.1.0-beta.1 | Introduced, as page.tsx, page.jsx or page.js. |