react-server675fbba4
react-servertreemaindocssrcpagesen(pages)routerstatic.page.mdx
docs/src/pages/en/(pages)/router/static.page.mdxmdx9.3 KiBdb84b570

title: Static generation category: Router order: 10

import Link from "../../../../components/Link.jsx";

Static generation

When you mark a page as static the file-system based router will add the page as an export to @lazarv/react-server's exports.

To mark a page as static, create a file with the matching path for the page with the .static.{js,mjs,ts,mts,json} extension and export default an array, a function returning an array, or an async generator function yielding the possible parameters for that route. The function can also be an async function.

For pages without any parameters, export default true.

export default true;

The smallest possible way to mark a page as static is by creating a .static.json file defining true.

true

Warning: you can't use true as the value for routes with parameters. You need to define an array of possible parameters or a function returning an array of possible parameters. If you use true for a route with parameters, the build will fail.

<Link name="dynamic-routes"> ## Dynamic routes </Link>

For dynamic routes, if you have a page at /users/:id you can create a file at /users/[id].static.ts with the following content:

export default [{ id: 1 }, { id: 2 }, { id: 3 }];

You can either export an array of route parameters or an async function returning an array of route parameters.

export default async () => [{ id: 1 }, { id: 2 }, { id: 3 }];

Your function will be executed at build time and the result will be used to generate the static pages.

<Link name="streaming-static-paths"> ## Streaming static paths </Link>

For routes with very large path counts — paginated content sourced from a CMS, slugs read from a large database, anything where materializing the full list before rendering starts is wasteful — declare the default export as an async function* (an async generator) and yield parameter descriptors lazily. Peak memory stays at one descriptor regardless of the total count, and rendering can start as soon as the first item is yielded.

export default async function* () {
  let cursor = null;
  do {
    const { items, next } = await fetchUsers(cursor);
    for (const user of items) {
      yield { id: user.id };
    }
    cursor = next;
  } while (cursor);
}

You can yield { ...params } descriptors (mapped through the route's parameter template, as above) or { path: "/explicit/path" } descriptors for full control. Sync function* generators work the same way for fully synchronous sources.

Note: detection is by function kind. Write async function* (or function*) directly as the default export — wrappers that return an ordinary function fall back to the array contract.

<Link name="static-json-data"> ## Static JSON data </Link>

You can use static JSON data for your static pages by creating a file with the .static.json extension.

For example, if you have a page at /users/:id you can create a file at /users/[id].static.json with the following content:

[{ "id": 1 }, { "id": 2 }, { "id": 3 }]

Note: we define static routes independently from the page component to separate the concerns of routing and page rendering. This way router doesn't need to import the code defining your page component during build time, which can be useful if you have a large dependency tree for your page component or your code has side-effects.

<Link name="override-static-paths"> ## Override static paths </Link>

You can override all static paths by defining an export() function in your @lazarv/react-server configuration file. This function will be called with an array of all static paths and you can return a new array of paths to override the default static paths. In this example, we remove the /en prefix from all static paths.

export default {
  export(paths) {
    return paths.map(({ path }) => ({
      path: path.replace(/^\/en/, ""),
    }));
  },
};

You can also use this function to add new static paths or remove some paths.

export default {
  export(paths) {
    return [
      ...paths,
      { path: "/new-page" },
    ];
  },
};
export default {
  export(paths) {
    return paths.filter(({ path }) => path !== "/en");
  },
};
<Link name="streaming-export"> ## Streaming the export path source </Link>

The regular-function form of export receives an array — every path the file-system router resolved, all at once. That's fine for most apps, but for very large exports (tens of thousands of pages, paginated content sourced from a CMS, on-the-fly path generation) materializing the full list before rendering starts is wasteful: peak memory grows with the path count, and the first render can't start until the last path is collected.

Declare export as an async function* (an async generator) and you opt into streaming instead. The generator receives the live AsyncIterable of paths produced by the file-system router and exportPaths, and it can yield paths to the renderer one at a time. The renderer pulls paths only when a worker is free, so the generator stays one step ahead of the export — peak memory is O(one path descriptor) regardless of the total count, and rendering starts on the first yielded path. Pairs naturally with --export-concurrency for parallel rendering.

export default {
  async *export(paths) {
    // Pass through whatever the file-system router resolved.
    for await (const p of paths) {
      yield p;
    }

    // Then yield additional paths lazily — e.g. fetched page-by-page from
    // a CMS, computed from a large database query, or read from a file.
    let cursor = null;
    do {
      const { items, next } = await fetchPage(cursor);
      for (const item of items) {
        yield { path: `/posts/${item.slug}` };
      }
      cursor = next;
    } while (cursor);
  },
};

Note: detection is by function kind. You must write async function* (or function*) directly as the value of export — wrappers that return an ordinary function (memoization, decorators, …) fall back to the legacy array-transform contract. The regular-function form is unchanged and remains the right choice for "give me the array, let me return a transformed array."

<Link name="static-export-api-routes"> ## Static export API routes </Link>

You can also export API routes as static routes. To do this, you can define your static path as an object with the path, filename, method and headers properties, where path is the route path, filename is the filename for the static file, method is the HTTP method for the request and headers is an object with the headers for the request. method and headers are optional.

export default {
  export() {
    return [
      {
        path: "/sitemap.xml",
        filename: "sitemap.xml",
        method: "GET",
        headers: {
          accept: "application/xml",
        },
      },
    ];
  },
}
<Link name="remote"> ## Static export micro-frontend routes </Link>

You can also export micro-frontend routes as static. To do this, you can define your static path as an object with the path and remote properties, where path is the route path and remote is a flag to indicate that the route is a micro-frontend route and the remote response payload needs to be generated at build time. By using static export for micro-frontends, you can improve the performance of your application by pre-rendering the micro-frontend content at build time.

export default {
  export() {
    return [
      {
        path: "/",
        remote: true,
      }
    ];
  },
};
<Link name="static-export-outlets"> ## Static export outlets </Link>

You can also export outlets as static. To do this, you can define your static path as an object with the path and outlet properties, where path is the route path and outlet is the name of the outlet. By using static export for outlets, you can improve the performance of your application by pre-rendering the outlet content at build time. Exported outlets will be rendered as RSC components. Client-side navigation to an exported outlet will use the static outlet content instead of making a request to the server.

export default {
  export() {
    return [{ path: "/photos/1", outlet: "modal" }];
  },
};
<Link name="disable-static-export-for-rsc-routes"> ## Disable static export for RSC routes </Link>

You can disable static export for RSC routes by setting the rsc property to false. This is useful if you have a page that is an RSC route but you don't want to pre-render it at build time or you don't plan to use the RSC payload for that route. This will prevent the RSC payload from being generated at build time and the route will be rendered only as a regular HTML page to reduce the deployment size.

export default {
  export() {
    return [{ path: "/photos/1", rsc: false }];
  },
};