Project Structure
A tour of the files and folders in a GioJS app.
A new project is intentionally small. Everything is driven by file conventions under app/.
my-app/
app/
layout.tsx # root layout: imports ./globals.css, exports metadata
globals.css # global styles (the CSS pipeline bundles and minifies them)
(site)/ # route group: no URL segment
layout.tsx # the site's navigation and footer - hydrated, so its links soft-navigate
page.tsx # the / route
about/page.tsx # the /about route
posts/[id]/page.tsx # dynamic route -> /posts/:id
not-found.tsx # 404 page
error.tsx # error page
components/ # your shared components (layout/SiteShell, layout/Navbar, layout/Footer)
public/ # static files, served at the site root and under /public/
fonts/ # the self-hosted .woff2 files gio.toml's [[fonts]] name
gio.toml # server configuration (fonts, images, security, ...)
.env.example # the environment variables the app reads - copy to .env.local
.gitignore # node_modules/, .gio/, .env*.local, build output
AGENTS.md # how the framework works, for coding agents
package.json # dev / build / start scripts
tsconfig.json # includes .gio/routes.d.ts for typed routes
.gio/ # generated at startup (route types, client build, caches) - not committedThe app directory
Routes are folders. A page.tsx (or .jsx) makes a folder a route; a layout.tsx wraps the pages beneath it. Dynamic segments use [brackets] ([...slug] and [[...slug]] for catch-alls), (group) folders organize routes without adding a URL segment, and _private folders are never routable. Any folder can also hold not-found, error and loading files for its part of the tree - see Layouts & Pages and File Conventions.
The root layout
app/layout.tsx renders the <html> document every page shares. It imports the global stylesheet and exports the site's default metadata - a title template that each page's own metadata fills in:
import type { LayoutProps, Metadata } from '@gio.js/core';
import './globals.css';
export const metadata: Metadata = {
title: { default: 'My App', template: '%s | My App' },
description: 'A GioJS application.',
};
export default function RootLayout({ children }: LayoutProps) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>{children}</body>
</html>
);
}The root layout is server-rendered HTML that never hydrates: put interactive components and context providers in a nested layout or the pages. That is why the starter's navigation lives in app/(site)/layout.tsx: a GioLink there prefetches and soft-navigates, while in the root layout it would be a plain link. The 404 and error pages sit outside the group and render the same SiteShell themselves. CSS can be imported from any page, layout or component, including CSS Modules - see CSS & Styling.
gio.toml
Server configuration, read once at startup. Every key is optional and an unknown key stops the server with a hint, so typos never go unnoticed. The starter self-hosts its fonts through [[fonts]]: the .woff2 files ship in public/fonts/, are copied into .gio/fonts/ at every start (no network needed) and are served from /_gio/fonts with preload links, instead of loading from a third-party CDN on every visit:
#:schema ./node_modules/@gio.js/server/gio.schema.json
[app]
name = "my-app"
[server]
host = "0.0.0.0"
port = 3000 # the PORT or GIO_PORT env var overrides it
http2 = true
[images]
allowed_widths = [640, 828, 1080, 1200, 1920]
quality = 80
[[fonts]]
family = "Fraunces"
url = "/public/fonts/fraunces-400-normal.woff2"
[[fonts]]
family = "Fraunces"
url = "/public/fonts/fraunces-400-italic.woff2"
style = "italic"
[[fonts]]
family = "JetBrains Mono"
url = "/public/fonts/jetbrains-mono-400-normal.woff2"
[[fonts]]
family = "JetBrains Mono"
url = "/public/fonts/jetbrains-mono-600-normal.woff2"
weight = 600The #:schema line gives editors autocomplete and inline docs. A font url can also be an https:// address, downloaded on the first start - see Font Optimization and gio.toml Configuration.
.env.example and .gitignore
.env.example lists the variables the app reads, with comments - copy it to .env.local for your own values. .env*.local files and .gio/ are git-ignored. (The scaffolder's package carries the file as _gitignore, because npm drops files named .gitignore from published packages, and renames it when it creates your project.) See Environment Variables.
public/
Files in public/ are served directly by the Rust layer - images, stylesheets, fonts. Static files never touch Node. Files answer at the site root as well as under /public/*: public/robots.txt is both /robots.txt and /public/robots.txt, so favicon.ico, manifest.json, apple-touch-icon.png, and .well-known/ files land where browsers and crawlers look for them. gio export writes public/ to both places in out/ too.
- A public file wins over a page with the same path (the Next.js precedence). In a static export, where a public file and a rendered page would need the same output file (public/index.html and app/page.tsx), the page is kept and the export lists the file as skipped.
- Not served at the root: dotfiles (except under .well-known/), symlinks, and a top-level public/_gio/ (the server's internal namespace). These stay reachable under /public/* only. Directory listings are never served.
- Guards, header rules, and
[[rate_limits]]written for a file's /public/... URL also apply at its root URL, so protecting /public/members/* protects /members/* too, and /_gio/image serves the file only to visitors those guards admit. Redirects and rewrites match only the URL requested - see Middleware. - Root-served files use
Cache-Control: public, max-age=0, must-revalidatewith Last-Modified, so browsers revalidate instead of keeping an old copy after a deploy. - The set of root-served files is indexed at startup, so the request path never pays a filesystem lookup. In development, edits to public/ refresh the index; in production, files added after startup need a restart.
Optional files
middleware.ts- redirects, rewrites, headers and guards in TypeScript (Middleware)gio.config.ts- Node plugins and their hooks (Configuration)app/sitemap.ts,app/robots.ts,app/manifest.ts- generated SEO files (Metadata & SEO)route.tsin any folder - an API endpoint (Route Handlers)