Known Limitations
What GioJS does not do yet, or does differently from Next.js - and what to use instead. Check this list before committing to a design.
GioJS is in public beta. Everything on this page is a current, deliberate limit of the framework rather than a bug; bugs belong in GitHub issues. Fixed bugs and what changed in each version are on the releases page.
Rendering model
- No React Server Components. There is no
'use client'/'use server'split and no async components: every page and nested layout is server-rendered and hydrated in the browser. Load data ingetServerSideProps- it and everything only it imports are stripped from the client bundle - and keep secrets out of the props it returns, which ship with the page. - No Server Actions. Mutations are page actions (
export async function action) posted by plain forms or<GioForm>, orroute.tshandlers called withfetch(). - The root layout never hydrates.
app/layout.tsxis server-only HTML. Context providers, state and event handlers go in a nested layout or the pages; links in the root layout are plain anchors (full-page navigation). Inline scripts there needcspNonce()under a CSP. - No data cache. Caching is per page, in the Rust server (
export const revalidate, tags,revalidatePath/revalidateTag). There is nofetch()cache,unstable_cacheor'use cache'; cache data yourself where a page cache is too coarse. - No hot module replacement. In development an edit restarts the Node worker and reloads open tabs (about 1.5 seconds), so client state such as form input is lost on each change. There is no React Fast Refresh.
- Node only. There is no edge runtime: pages, route handlers and actions run in a Node 20+ worker process.
Routing and middleware
- The
app/directory is the only router. A Next.jspages/app is converted by the migration tool. - Not supported:
templatefiles, parallel routes (@slotfolders) and intercepting routes. Catch-alls, optional catch-alls, route groups, private folders and per-folderlayout,loading,errorandnot-foundfiles are - see Layouts & Pages. - Middleware is declarative.
middleware.tsandgio.tomldefine redirects, rewrites, headers and guards that the Rust server evaluates before routing; no JavaScript runs there per request. Per-request logic belongs ingetServerSideProps, a route handler, or a Node plugin'sonRequesthook ingio.config.ts. See Middleware. - Locale detection, not translations. i18n routing detects the locale from the path, a cookie or
Accept-Languageand hands it to your code; message catalogs and formatting are up to a library of your choice. Domain-based locales are not supported. See Internationalization.
Metadata and images
- No generated Open Graph images. There is no
opengraph-image.tsx/twitter-image.tsxconvention that renders JSX to PNG. PointopenGraph.imagesat a file inpublic/or at an image aroute.tshandler produces. - No file-based icons (
app/icon.png), no separateviewportexport, and only one sitemap (app/sitemap.ts; nogenerateSitemaps). Put icons inpublic/and link them frommetadata.icons. See Metadata & SEO.
Running several instances
Every piece of server state lives in one server process. There is no shared backend (Redis or otherwise) yet, so with several instances behind a load balancer:
- The page cache is per instance. Each instance renders and caches its own copy, and
revalidateTag/revalidatePathpurge only the instance whose worker calls them - callPOST /_gio/revalidateon every instance. See On-demand revalidation. - Rate limits are per instance.
[[rate_limits]]buckets are kept in memory, so N instances allow up to N times the configured rate. - WebSocket rooms are per instance.
broadcast(room, ...)reaches sockets on every worker of one server, but not on other servers - fan out through your own pub/sub for a multi-instance chat. - Sessions are unaffected: they live in an encrypted cookie, so any instance with the same
GIO_SESSION_SECRETreads them.
Static export
gio export produces HTML plus hydration - pages are interactive and GioLink navigation works - but there is no server behind it. These need the GioJS server and are skipped or inert in an export:
route.tshandlers, server-sent events, WebSockets, page actions and form posts- per-request
getServerSideProps(it runs once, at export time), redirects it returns,notFound()pages, and dynamic routes withoutgetStaticPaths - caching and revalidation,
gio.tomlandmiddleware.tsrules (redirects, rewrites, headers, guards), sessions, rate limits - security headers, CSP nonces and CSRF checks - configure headers on the static host instead
- image optimization:
GioImagerenders its plainsrc
See Static Export.
Content-Security-Policy
- Nonces need the GioJS server. The Rust server stamps a fresh nonce into every response, cache hits included; a static export has no server to do it, and
cspNonce()returnsundefinedthere. - Styles need
'unsafe-inline'. Reactstyleprops and view-transition styles cannot carry a nonce, so a nonce-basedstyle-srcbreaks them. Scripts are fully nonce-protected. - No self-compressed responses. While a CSP uses
{nonce}, a route handler response that sets its ownContent-Encodingis refused with a 500, because the server cannot place the nonce in a body it cannot read. Let GioJS compress it.
Platforms and packaging
- Prebuilt server binaries exist for Linux x64 (glibc and musl), macOS (Intel and Apple Silicon) and Windows x64. Linux arm64 (Graviton, Ampere, Raspberry Pi) is not published yet: build the server from source (
cargo build --release --locked -p giojs-server, with the Rust version fromrust-versioninCargo.tomlor newer) and hand it togio build standalonewithGIO_STANDALONE_SERVER_BIN, or buildlinux/amd64container images. Windows on ARM and FreeBSD have no binary either. - Standalone builds are frozen. A
gio build standalonefolder bakes in the framework version and theGIO_PUBLIC_*values: framework updates and public variable changes need a rebuild. See Standalone Deploys.
Requests and sessions
- Request bodies are buffered. Uploads are read whole into memory, capped by
[server] max_body_bytes(2 MiB by default) and, because the body crosses to the worker in one piece, at roughly 48 MiB whatever the setting -0included, which means no limit of its own. Upload large files straight to object storage with presigned URLs. See Forms and Mutations. - Sessions are cookie-only. The whole session is encrypted into one cookie, so it is limited to what fits in 4 KB, and there is no server-side session store to revoke one session early: keep ids and small flags in it, and check revocation against your database where it matters. See Authentication.
Missing something that is not listed here, or hit one of these limits in a way the workaround does not cover? Open an issue at github.com/Ggaming5005/GioJS with the GioJS and Node.js versions and a minimal reproduction.