What's new in each version of GioJS. Updated on every release and patch.
0.1.0-beta.8
latest
The production release: security on by default with a gio.toml switch for every protection, sessions and forms, a worker pool, on-demand revalidation, a metadata API, CSS Modules, a testing kit, a Next.js migration tool, a real gio CLI, a starter with optional features, and a rebuilt documentation site with search and a full API reference. Tested on Node.js 20, 22 and 24.
Security
Every response carries default security headers; a Content-Security-Policy with fresh per-response nonces is one line of gio.toml - cache hits, PPR shells and streamed responses included. Cross-site POST/PUT/PATCH/DELETE requests and cross-origin WebSocket upgrades are refused in Rust before Node sees them.
Encrypted, signed cookie sessions (createSessionStorage), cookie and signing helpers, and require_session guards that verify the session in the Rust layer before any Node code runs.
Trusted proxies: real client IPs for rate limits, metrics and req.ip, plus request ids on every response and log line in both processes.
Production mode is anything but NODE_ENV=development: error responses carry only a digest that matches the log line. Connection caps and slowloris/TLS/body timeouts, Host- and Origin-gated dev endpoints, a closed /_gio namespace, and supply-chain hardening (committed Cargo.lock, cargo-deny, pinned CI actions, SECURITY.md).
Every protection and feature is on by default and has a gio.toml switch - [security.csrf] enabled, [security] default_headers, [cache], [images], [prefetch], [health], [env] and more. Each loosened protection logs a startup warning that giojs-server --check-config reports too; the Security switches guide lists them all, and the few that stay fixed on purpose.
Fail closed: a middleware.ts that throws, a guard or rule that cannot be enforced, or an invalid [i18n] setting stops startup instead of being skipped, and --check-config lists every problem in one run with its line. A worker that cannot boot ends startup with its own error.
Routing, data and rendering
Catch-all and optional catch-all segments, route groups, private folders, layouts by folder ancestry, and per-folder not-found, error and loading files with notFound(). Router hooks (usePathname, useParams, useSearchParams, useRouter) and a persistent client root that keeps shared layout state across soft navigations.
Page actions and <GioForm>: forms that post to the page, work without JavaScript, and upgrade to client-side submissions with validation errors and Post/Redirect/Get.
On-demand revalidation: tag pages, purge with revalidateTag() / revalidatePath(), or call POST /_gio/revalidate from a CMS webhook. HTML responses get Cache-Control and ETags.
WebSocket route params, rooms and connection auth; streamed route handler responses; every Set-Cookie header survives the Rust-Node boundary.
.env files, GIO_PUBLIC_* variables in client code, and a server-only guard that turns a leaked server import into a build error.
Styling, assets and SEO
CSS imports from any component and CSS Modules, bundled and minified by the CSS pipeline; public/ served at the site root; CSS that revalidates instead of going stale.
A metadata API (metadata / generateMetadata with title templates), app/sitemap.ts, robots.ts and manifest.ts, and a <JsonLd> component. GioImage srcsets follow the [images] widths in gio.toml.
Static export hydrates: exported pages are interactive and navigate client-side on any static host.
Operations and developer experience
A supervised pool of Node render workers ([server] workers = N or "auto"), workers that can never be orphaned, JSON logs, and Prometheus metrics labeled by route pattern.
gio.toml is strict - an unknown key stops startup with the closest valid one - and ships a JSON Schema for editor autocomplete; PORT and GIO_HOST/GIO_PORT are honored.
@gio.js/core/testing (renderPage, callRoute, createTestServer), typed app conventions (PageProps, LayoutProps, Metadata...), whole-project dev watch, and create-giojs migrate for Next.js projects.
New guides - environment variables, deploying to Docker, Fly.io, Railway, Render and a Linux server, a production checklist - and a list of known limitations.
Reproducible builds: the same code always builds the same client chunks, so the deployment ID stays put across restarts and pods. CI tests Node.js 20, 22 and 24.
Documentation
A rebuilt docs site: one API reference page per component, hook, function, file convention, page export, gio.toml section and CLI command, instant search (Ctrl/Cmd+K), an on-this-page outline, and copy-as-Markdown on every page.
New guides for upgrading, streaming, redirecting, Content Security Policy and turning protections on and off.
CLI and starters
A real gio CLI: gio dev and gio start (with --port, --host and --open), gio routes, gio typegen, gio doctor and gio info, plus gio migrate and gio add. giojs-server --check-config validates a deploy's configuration without starting it.
npm create giojs takes a target directory, --pm, --no-git and --force, and its starter imports its CSS, self-hosts its fonts and declares page metadata. Optional starter features - Tailwind CSS, an API route with a form, authentication, a SQLite database, Docker and CI - come from flags at creation or create-giojs add / gio add later.
Upgrading from beta.7
Unknown or never-implemented gio.toml keys ([cache] memory_mb, [cache.redis], ...) now stop the server with a hint - fix or remove them.
Cross-site form posts are refused by default: list OAuth form_post, SAML and payment-provider callbacks in [security.csrf] exempt, and other origins of yours in trusted_origins.
Behind a reverse proxy, set [server] trusted_proxies so rate limits and req.ip see visitors, and [security] hsts = true when the proxy terminates TLS.
The Node worker follows the server's mode: an unset NODE_ENV is production on both sides (production React build, no error details in responses). Run the dev server with NODE_ENV=development, as npm run dev does.
A page with revalidate that reads ctx.cookies, the cookie/authorization header or the client's IP or host is no longer cached - it used to be stored and served to everyone. Drop revalidate, or cache the shell with shell = 'cache' and personalize inside Suspense holes.
Every response now sends X-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff and a Referrer-Policy - override or remove them in [security.headers] - and req.json() in a route handler answers 415 unless the body was sent as JSON.
Routing follows the App Router: _private folders are never routed, (group) folders leave the URL, catch-all params are one /-joined string, and conflicting routes stop startup. public/ files are served at the site root and win over a page with the same path.
In production, error.tsx receives a generic message and a digest, and it now also runs in the browser as an error boundary, so it must not import server-only code.
Idle HTTP/1.1 keep-alive connections are closed after 10 seconds: keep a pooling proxy's upstream idle timeout below that, or raise header_read_timeout_secs and idle_timeout_secs.
Bare gio no longer starts a server - it prints the help and exits with code 2. Use gio start or gio dev. giojs-server refuses arguments it does not take (exit 2): configure it with gio.toml and environment variables.
Rules fail closed: a middleware.ts that throws, a guard without a requirement, a redirect, rewrite or header rule that cannot compile, or an invalid [i18n] setting now stops startup instead of being skipped.
A [metrics] section without a token or ip_allowlist answers only this machine; list your scrapers in ip_allowlist. And 0 now lifts a limit everywhere in gio.toml ([prefetch], [websocket] max_connections, [server] max_body_bytes ...) where it used to refuse.
0.1.0-beta.7
Partial prerendering - cached shell, per-user Suspense holes streamed into the same response - and standalone deploys: one self-contained folder that runs on any server with only Node installed.
Partial prerendering (PPR)
export const shell = 'cache' next to revalidate splits a Suspense page: the pre-Suspense shell is cached in Rust and served instantly, while the holes re-render per request (getServerSideProps reruns with the requester's own cookies) and stream in behind it. The contract: the shell renders identically for every visitor - only Suspense content may be personalized.
Degrades gracefully - a failed holes render ends the body after the shell with the Suspense fallbacks still visible - and X-Gio-Cache reports it all: ppr; shell=stored / hit / stale.
Standalone deploys
gio build standalone packages the app into one folder: the Rust server binary, the whole Node side bundled to a single worker.js (React included, no tsx/esbuild at runtime), a run.mjs launcher, hydration chunks, and public/. Copy it to any server with only Node installed and run node run.mjs - no node_modules, no npm install.
--target cross-builds for any installed @gio.js/server-<platform> package: build on Windows or macOS, deploy to a Linux VPS.
Fixed
latest-tag promotion retries through npm registry propagation lag instead of silently skipping, and the scaffold's typecheck config was fixed (Bundler moduleResolution + @types/node).
0.1.0-beta.6
Repaired npm publishing (beta.5 shipped broken packages), cache-poisoning and SSRF fixes, and a developer-experience wave: Rust-executed middleware rules, streaming SSR, typed routes, cache observability, gio bench, and a smarter dev overlay.
Release integrity & security
Emergency npm repair: beta.5 published @gio.js/react without dist/ and create-giojs without its bin targets. The release workflow now builds before publishing, a tarball gate refuses to publish packages with missing entry points or binaries, tag pushes run the full test matrix first, and latest-tag promotion covers the platform binaries.
Security: background revalidation can no longer cache a cookie-personalized page under the shared key (cache poisoning); the image optimizer allowlist is WHATWG-parsed, closing an SSRF that reached internal IPs through crafted URLs; plus decode limits, rate-limited /_gio/image, and header sanitization fixes.
A restart no longer throws away the disk cache: deployment IDs are content-derived (pin with GIO_DEPLOYMENT_ID), so identical builds keep their cache warm.
Developer experience
Middleware: declarative redirects, rewrites, response headers, and cookie guards from gio.toml and/or middleware.ts (defineMiddleware), with :param / *rest patterns and substitution - compiled and executed in Rust before routing, so no request header can bypass them.
Streaming SSR: personalized (uncacheable) pages flush React's shell as soon as it renders and stream Suspense content in the same response, instead of buffering the full document - cacheable pages keep the buffered path and serve from cache at memory speed.
Typed routes: .gio/routes.d.ts is generated from your app/ directory at boot, and href('/posts/:id', { id }) autocompletes and typechecks with zero annotations.
Cache observability: every response carries X-Gio-Cache (hit / stale / miss / bypass / static with ttl and age details), and gio cache explain <url> decodes it in plain English.
gio bench: a zero-dependency load generator reporting req/s, p50/p90/p99/max latency, and the X-Gio-Cache label of what it measured - single URL or --suite table mode.
The dev error overlay shows codeframes for project frames, and stack file:line links open your editor (GIO_EDITOR/VISUAL/EDITOR).
/_gio/health now reports deploymentId, nodeReady, cacheEntries, and uptimeSecs; <GioLink prefetch="viewport"> prefetches when a link scrolls into view; the docs serve llms.txt and scaffolds include AGENTS.md.
0.1.0-beta.5
The big one: client-side hydration, API routes, dev watch mode, worker supervision, and a hardened Rust↔Node boundary.
New
Client-side hydration: per-route esbuild bundles, a #__gio hydration boundary, and props serialized safely into the page - interactive React with zero hydration-mismatch surface. getServerSideProps and its server-only imports are stripped from client bundles.
API routes: route.ts files export GET/POST/PUT/PATCH/DELETE handlers receiving params, query, headers, parsed cookies, and the request body - return JSON, a web Response, or a GioEventStream (SSE). Unexported methods get a proper 405.
getServerSideProps now receives the full request (method, path, headers, cookies) and can return response headers like set-cookie - such pages are automatically uncacheable.
app/not-found.tsx and app/error.tsx render real 404/500 pages through your layouts; static export writes 404.html so hosts like Cloudflare Pages return a real 404 for unknown URLs instead of the home page.
Dev watch mode: edit a file and the server clears caches, restarts the worker, and reloads your browser - about 1.5 seconds edit-to-browser.
Alpine/musl Linux binaries (@gio.js/server-linux-x64-musl) with automatic libc detection.
Reliability & security
The Node worker is supervised: crashes respawn in ~300 ms instead of taking the server down, and a hard-killed server can never orphan the worker (Windows Job Objects).
Renders are never shared across users: coalescing is credential-aware and only ever shares explicitly cacheable pages.
The IPC boundary is versioned (enforced at handshake), authenticated with per-instance tokens, and carries request bodies - binary-safe - plus vary/cacheTags/cancel frames.
Client disconnects and timeouts now abort in-flight React renders instead of finishing work nobody reads.
All @gio.js/* packages, platform binaries, and templates are version-locked; releases publish with npm provenance; CI runs a real Rust↔Node integration suite on Linux and Windows.
0.1.0-beta.4
Packaging and clean-install fixes following beta.3.
Fixed
Clean-install issues found while testing the published packages end-to-end: tsx as a runtime dependency, package file lists, and template fixes.
0.1.0-beta.3
SEO-ready static exports.
New
Static export auto-generates robots.txt and a full sitemap.xml (absolute URLs from GIO_SITE_URL).
Exported pages no longer reference a client bootstrap script that 404s on static hosts.
0.1.0-beta.2
Static export - build to plain HTML and deploy anywhere, for free.
New
Static export: gio export pre-renders your whole app to out/ as plain HTML - deploy free to Cloudflare Pages, GitHub Pages, or any static host.
create-giojs now asks "Server app or Static site?" and wires npm run build accordingly (gio export for static).
getStaticPaths() convention to pre-render dynamic routes during export.
getServerSideProps runs at build time, baking its data into the exported HTML.
0.1.0-beta.1
First public beta on npm, published under the @gio.js scope.
Highlights
Published to npm: @gio.js/server, @gio.js/core, @gio.js/react, create-giojs, and prebuilt platform binaries (linux-x64, win32-x64, darwin-x64, darwin-arm64).
npm create giojs@latest - interactive scaffolder with an arrow-key picker for TypeScript / JavaScript.
Framework
Rust HTTP/2 server with brotli/gzip compression, static file serving, and rustls TLS.
Image optimization endpoint (/_gio/image): AVIF → WebP → JPEG with a two-layer cache.
ISR page cache with stale-while-revalidate and deployment-aware invalidation.
React SSR via renderToReadableStream, getServerSideProps, nested layouts, and file-based routing for .tsx / .jsx / .js.
Route handlers, Server-Sent Events, and WebSockets over a dedicated IPC pipe.
Self-hosted fonts (WOFF2), i18n routing, Prometheus metrics, and a dev dashboard.
Subscribe on GitHub to be notified when a new version ships.