question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Static rendering all the routes, even the parameterized ones

See original GitHub issue

This is my exploration into the idea of static rendering all the routes such that they can be served in a totally language agnostic way. This is of course easy to do already for routes with no route params, but not so simple for the parameterized routes.

Why would I want to pre-render a parameterized route, where all the data comes from the clientside XHR call? Aren’t I just prerendering a blank page? Not at all my friend, you prerender as much as you can which would typically include at least the global nav bar. Having that appear instantly instead of merely quickly upon hydration makes all the difference in giving your site an app-like feel.

And in my case it’s especially important since I’m not using client-side routing, so each page load needs to be as fast-feeling as possible. Why no client-side routing? Imagine a large site with 1000 pages, an in-house project spanning many years and having many separate teams. Building this as a single app is bound to result in slow build times, so you can split it up into 10 Svelte projects where each has 100 pages (all behind a reverse proxy). But as long as you’re splitting it at all it makes sense to just split it completely with full page reloads on every transition. Now you can reproportion ownership fluidly as you see fit, and most importantly, you can iteratively migrate page by page to some new framework. Perhaps whatever the hot framework of 2025 is. This a whole separate topic I could say a lot about but let’s leave it at that.

So anyway, the '*' you pass to kit.prerender.pages only does the parameterless routes. A first attempt at this might be to add another route in there that has garbage for the param since it won’t even be looked at from the serverside, and updating your code that runs onMount to only look at window.location instead of the page store.

I put together a repo to try out some ideas, at https://github.com/johnnysprinkles/static-all-the-things

Setup

The first commit is just setting things up with a small constellation of services to make a more realistic prod-like setup. There’s SvelteKit, there’s a simple API server, and there’s a web server that serves the adapter-static compiled assets off of disk. Just to ensure we have a complete break between build time and runtime the web server is written in Python. Could be anything (for me it’ll probably be Java or Kotlin), but all the matters is it’s not Node.

On the SvelteKit side I added two routes related to airplanes, one is a parameterless List page, and one is a detail page that has an [id] in its route.

Screen Shot 2021-05-23 at 4 45 19 PM

https://github.com/johnnysprinkles/static-all-the-things/commit/b06f612a31b4009e5a44a4b05b97f7c7cffd37f1

This example just gives you a 404 for the parameterized routes.

First pass

https://github.com/johnnysprinkles/static-all-the-things/commit/3be17a2a84147cdc516f83c756fd72ebe021ce65

  pages: [
    '*',
    '/airplane/XXX',
  ],

This manually adds preprender paths with garbage for the parameters, and it does work. I can run it and my dynamic pages function. It’s all hacked up and manual though, we can do better.

First attempt at interpolating

We’d really like to keep using the store, which should be a source of truth for route params and other request-specific data, instead of going around it to inspect the window.location. So instead of passing garbage for the params when server-side rendering, what about if we pass something meaningful? Such as just the param name in square brackets? (I know, this is kind of overloading the meaning of square brackets, but it’s just a start).

If we do that, we can just have the web server replace e.g. [id] with the actual ID. A hacked up and manual version of this is in:

https://github.com/johnnysprinkles/static-all-the-things/commit/5ed2e708ec1a355788b8bfb9deb24ff70d51b8e2

  pages: [
    '*',
    '/airplane/[id]',
  ],

More automated way of interpolating

We can tidy things up a bit by taking advantage of the manifest.js file, which knows both the names of all the route params (in JS comments) and the regexes that match, e.g.

export const routes = [
    // src/routes/index.svelte
    [/^\/$/, [c[0], c[2]], [c[1]]],

    // src/routes/networking/addresses/list.svelte
    [/^\/networking\/addresses\/list\/?$/, [c[0], c[3]], [c[1]]],
    ...

So the next version of this reads and parses that:

https://github.com/johnnysprinkles/static-all-the-things/commit/22e3e98149876364d8a4317e9557f727e2287021

It would be nice if we had routes presented in a language agnostic way, such as a routes.json file that lives next to the manifest. A possible feature request.

Once we have that we can fill in the page store via regex slice and dice. This would be a little easier if the page store data was pulled out of that structure (and also perhaps surrounded by special comment tokens). So for example we currently get this:

<script type="module">
  import { start } from "/./_app/start-aec04e6a.js";
  start({
    target: document.querySelector("#svelte"),
    paths: {"base":"","assets":"/."},
    session: {},
    host: location.host,
    route: false,
    spa: false,
    trailing_slash: "never",
    hydrate: {
      status: 200,
      error: null,
      nodes: [
        import("/./_app/pages/__layout.svelte-1240a5ff.js"),
        import("/./_app/pages/airplane/[id].svelte-a58c7efe.js")
      ],
      page: {
        host: location.host, // TODO this is redundant
        path: "/airplane/[id]",
        query: new URLSearchParams(""),
        params: {"id":"[id]"}
      }
    }
  });
</script>

But if it were more like this it would be easier for any language that can emit JSON to fill it in:

<script type="module">
  let pageStoreData = /** PAGE_STORE_START **/ {
    host: location.host,
    path: "/airplane/[id]",
    queryString: "",
    params: {"id":"[id]"}
  }/** PAGE_STORE_END **/;
  
  import { start } from "/./_app/start-aec04e6a.js";
  start({
    target: document.querySelector("#svelte"),
    paths: {"base":"","assets":"/."},
    session: {},
    host: location.host,
    route: false,
    spa: false,
    trailing_slash: "never",
    hydrate: {
      status: 200,
      error: null,
      nodes: [
        import("/./_app/pages/__layout.svelte-1240a5ff.js"),
        import("/./_app/pages/airplane/[id].svelte-a58c7efe.js")
      ],
      page: {
        host: pageStoreData.host,
        path: pageStoreData.path,
        query: new URLSearchParams(pageStoreData.queryString),
        params: pageStoreData.params
      }
    }
  });
</script>

So that’s where things are at now, this will be an ongoing exploration.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:39 (27 by maintainers)

github_iconTop GitHub Comments

2reactions
lsabicommented, Nov 15, 2021

To be honest, I expected crawl: true in the config to behave, as crawlAllowDynamicRoutes. Since it crawls in order to build the static site, it is to be expected that all links that are encountered are rendered.

If someone doesn’t want to pre-render the entire database, one should set the pages to be rendered in the config.prerender.entries. It’s simpler to config a couple of pages that need to be rebuilt (or need to be built for the first time), than to list the whole content of a database (which may be thousands of pages).

These are my two cents from reading the documentation, as I can’t find this problem mentioned anywhere in the docs.

1reaction
benmccanncommented, May 24, 2021

It’s not clear to me what you’re requesting. Do you have a concise way of stating it? Parameterized routes are currently prerendered, but must be either linked to or specified as an option

Read more comments on GitHub >

github_iconTop Results From Across the Web

Routing in Next.js – How to Set Up Dynamic Routing with Pre ...
In this tutorial, you'll learn how to set up dynamic routing in Next.js. You'll also learn about pre-rendering and why it's important.
Read more >
How to Create Pages in Next.js with Static & Dynamic Data
To do that, we're going to use getStaticPaths to define each route (or path) we want to render statically. Let's start off by...
Read more >
Static Site Generation with Next.js and TypeScript (Part IV)
js will pre-render a static page that can be accessed at the route /posts/:id (e.g., /posts/1 ). In the route, the named parameter...
Read more >
Server Rendering with React and React Router - ui.dev
In this comprehensive, up-to-date guide, you'll learn how, why, and when to add server rendering to a React and React Router application.
Read more >
How to access dynamic route parameters when preloading ...
I am attempting to render a dynamic route preloaded with data fetched via an async thunk . I have a static initialAction method...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found