Rendering modes

By default, Rakkas renders your page on the server and sends the rendered HTML to the client where the client-side application takes over and “hydrates” the page to make it interactive. When you use client-side navigation, only the code needed to render the new page will be fetched from the server (if it's not already in the cache) and the page will be rendered on the client. If you need new data to render the page, you fetch it in your load function, possibly sending one or more requests to your server (API endpoints) or other servers.

This default behavior can be changed: You can opt out of server-side rendering to use client-side rendering on certain routes or you can pre-render some pages during the build.

Opting out of SSR (client-side rendering)

App pages that are specific to each user rarely benefit from SSR. You can opt out of SSR for a route by passing false in options.ssr to definePage. You can also control the behavior of a group of pages by using options.ssr in defineLayout. Child layouts and pages can override their parent layout's setting.

When using client-side rendering, the server sends a placeholder HTML to the client and the client handles the rendering itself. By default, the placeholder HTML consists of the text "Loading..." which you probably want to customize by creating a placeholder.{jsx|tsx} file in your src directory. This file should default export a function which returns a ReactNode or a promise of one. You can use react-helmet-async as usual to control the head tags.

Please note that the placeholder nodes will not be hydrated so they cannot be interactive. This means CSS-in-JS solutions won't work. Currently, importing CSS files doesn't work either. You can use inline styles, <style> tags with dangerouslySetInnerHTML, or <link> tags with href pointing to a CSS file in the public directory for the time being.

In production mode, the placeholder HTML will be generated once during the build and the same HTML will be used for all pages. In development mode, it will be regenerated every time a page is requested.


Instead of generating on-demand, you can chose to render some of your routes upfront during the build which may speed up response times. It works by setting the prerender property in the configuration to an array of paths to be pre-rendered. During the build, Rakkas will render those pages into static HTML unless their load functions return prerender: false. Rakkas will also crawl the a and area elements in each page to discover more pages to pre-render unless the load function returns crawl: false.

Not every page is suitable for pre-rendering. You can do it if and only if the content of the page is solely determined by its URL path. In particular, if the content depends on any of the following, it is not suitable for pre-rendering:

  • Cookies: A static page can't show different contents to different visitors, so you can't have any sort of session management.
  • Query parameters: Pre-rendering works by saving the rendered HTML in the file system and serving it via a static file server. Serving different pages for different query parameters is not supported in general. Path parameters are fine, though. So, instead of a URL pattern like /books?page=2 you should prefer /books/page-2 if you intent to pre-render a page.
  • Ever-changing external APIs: If you're fetching from an external API in your load functions, there's a chance that what was fetched during the pre-render and during a client-side render will differ. This may be confusing for your visitors. If you're not interested in further updates after pre-rendering, you can solve this problem by creating a pre-rendered API endpoint (read on) and do the fetching there.

Pre-rendering API routes

You can return prerender: true from your your API endpoint responses to enable pre-rendering of data needed in your pages. In addition to the rules about pages above, only GET HTTP method is eligible for pre-rendering. An endpoint is considered for pre-rendering if it's in the prerender configuration option or if it is fetched from while rendering a page in the crawling phase.

since pre-rendering works by saving the pre-rendered data to the file system, it isn't possible to have two routes that would cause a directory and a file to have the same name. For example you can't pre-render both /api/articles and /api/articles/[articleId] routes because it would require api/articles to be a file and a directory at the same time. For pages, it is solved by saving pre-rendered pages as /directory/index.html instead of /directory.

The recommended way of side-stepping this problem is to use file extensions, like /api/articles.json and /api/articles/[articleId].json. It also helps the static file server get the content types right. In this case the endpoint handlers would be named /api/articles.json.endpoint.js and /api/articles/[articleId].endpoint.js respectively.

If your entire application is suitable to pre-rendering, you should consider using static as your deployment target.

Serverless environments

Note that pre-rendering is performed during the build. The environment will be different than the final deployment environment. You won't have access to features that are only available in the final deployment environment. So, for example, you can't use the Cloudflare Worker's KV store during pre-rendering.