develwoutacause’s avatardevelwoutacause’s Twitter Archive—№ 827

  1. The last week or so I've been exploring some cool ideas with using server-side rendering (#SSR) in #rules_prerender. Sounds simple, but actually has some very results, I'm really excited about this and I want to share why. 👇 github.com/dgp1130/rules_prerender/tree/ref/ssr/examples/ssr/
    1. …in reply to @develwoutacause
      #rules_prerender is already designed as a static-site generator (#SSG) built on #Bazel and supports bundling and loading of browser #JavaScript for client-side rendering (#CSR). Adding SSR is just one more aspect, but notice anything from this web page? github.com/dgp1130/rules_prerender/tree/ref/ssr/examples/ssr/mixed_component
      A screenshot of a web page with 6 bullets. In order they read:
1. SSG: Built in Bazel workspace `rules_prerender`.
2. SSR: This was request #1.
3. CSR: Viewport width is 822px.
4. SSG: Built in Bazel workspace `rules_prerender`.
5. SSR: This was request #1.
6. CSR: Viewport width is 822px.
      1. …in reply to @develwoutacause
        It includes SSG, SSR, and CSR, but *all three* are used on the same page, interchangeably. I even included environment-specific information to prove it and duplicated the items to show that any order is possible.
        1. …in reply to @develwoutacause
          This works by rendering everything possible at build-time, with placeholders for SSR content. When the server starts, it loads this content and parses out the SSR placeholders. Each request invokes the SSR piece and stitches it together in the full document.
          1. …in reply to @develwoutacause
            Not only that, but since SSR is a slice of a component, its content can compose *another* component using some combination of SSG / SSR / CSR. Here, we have an outer component's SSR slice wrapping an inner component with SSG and SSR data. Time travel! github.com/dgp1130/rules_prerender/tree/ref/ssr/examples/ssr/composition_component
            A simple web page with a nested list of the items.
First bullet: SSG: Outer component header.
Second bullet: SSR: Outer component.
Begin nested sublist.
First bullet: SSG: Inner component header.
Second bullet: SSR: Inner component called by Outer component.
Third bullet: SSG: Inner component footer.
End nested sublist.
Third bullet: SSG: Outer component footer.
            1. …in reply to @develwoutacause
              The server even supports streaming. Any SSG content is automatically streamed to the client and SSR content can support streaming by simply implementing a generator API. In this example, each item takes 50ms to load but is delivered to the client ASAP. github.com/dgp1130/rules_prerender/tree/ref/ssr/examples/ssr/streaming_component
              1. …in reply to @develwoutacause
                Not only that, but all (top-level) SSR components invoked concurrently. So even if two make slow database queries, they won't block on each other. Here, each item takes 1sec to load. Since they are started at the same time, they finish in 1sec, not 10! github.com/dgp1130/rules_prerender/tree/ref/ssr/examples/ssr/concurrent_component
                1. …in reply to @develwoutacause
                  The actual APIs are pretty simple IMHO. An SSR component is simply a class with a render function which returns a string. It defines build-time data required as input, registers itself with the server, and defines its types, much like #WebComponents. github.com/dgp1130/rules_prerender/blob/ref/ssr/examples/ssr/foo_component/foo_ssr.ts
                  1. …in reply to @develwoutacause
                    At build-time, we can "slot" in an SSR piece by simply giving the registered name and the required build-time data. github.com/dgp1130/rules_prerender/blob/ref/ssr/examples/ssr/foo_component/foo_prerender.ts
                    1. …in reply to @develwoutacause
                      We're even able to keep things (mostly) strongly typed, despite the philosophy that the different platforms (browser, server, build) should never cross-import each other. We cheat a bit by allowing type-only cross-imports... 🤫
                      A screenshot of Visual Studio Code which shows a TypeScript source file which renders some HTML at build-time and invokes an API to set up an SSR component to be called later. This passes a `name` field set to `0`, which is shown in editor to be a type error via a red squiggly underline. The terminal window also attempts a `bazel run` command which fails with the same type error, indicating that for the `name` field "Type `number` is not assignable to `string`."
                      1. …in reply to @develwoutacause
                        Async SSR is of course supported and streaming is done by simply making the render() function a generator. Just yield any HTML content you want to return and the server will handle the rest. github.com/dgp1130/rules_prerender/blob/ref/ssr/examples/ssr/streaming_component/streaming_ssr.ts
                        1. …in reply to @develwoutacause
                          I know SSG and SSR aren't new things, but I think the technology here is pretty cool, particularly how things can interleave in the same page seamlessly and how data is effortlessly transferred from a build-time context to a server context.
                          1. …in reply to @develwoutacause
                            As cool as this is, I'm not yet convinced this is a good idea. I'm still skeptical about the usefulness of intermixing SSG and SSR content on the same page. Is it that much worse to just SSR the whole page? Would you find this kind of feature useful?
                            1. …in reply to @develwoutacause
                              I had a lot of fun prototyping and playing around with this, so I wanted to share. I'm sure I'm not the first to try intermixing SSG and SSR on the same page but it worked out better than I expected and got me really excited. Let me know what you think! github.com/dgp1130/rules_prerender/discussions/44