JavaScript SSR made easy
Tags: javascript, ssr, vite, webdev
Published: 2023-02-21 18:24:40

Server Side Rendering (SSR) in any JavaScript framework, with preservation of the reactivity behavior of the application, depends on the ability to hibernate the runtime state (at server), so that it can be later (at client) hydrated back to its previous state. 

### Video
https://www.youtube.com/watch?v=HXmWYlhQCeU
(PS, this is my first video in a decade, I hope my coding skills make up for the video quality ;)

### Source code
[kitchen-sink](https://github.com/xania/view/tree/main/packages/kitchen-sink)

The following code snippet is a basic SSR simulation of how hibernation and hydration could work. 

```javascript
// ~/pages/hello-world.ts
export default function server() {
      console.log("Hello World");

   function client() {
      console.log("Hello World");
   }

   return client;
}
```

I was hoping I could use an existing solution for a UI library that I wrote called `@xania/view` but all solution that I could find are specific to other UI frameworks. 
So I decided to build a UI agnostic solution. It is extremely hard and it evolves writing code transpiler/compiler but I did it anyway and I am hyped to share with you the results.

## Introducing `vite-plugin-resumable`

This plugin is a UI framework agnostic solution for hibernating and hydrating javascript closures. You can say we found a way to serialize functions. It is the foundation for SSR feature of `@xania/view` but it can be used with any other UI library.


> Continue reading to learn how to setup your project or visit github [here](https://github.com/xania/view/tree/main/packages/kitchen-sink) for full working source code of the example.

### Setup
Currently we have only support for vite projects.

0. create new vite project
> npm init vite my-vite-app

1. install resumable package
> npm i vite-plugin-resumable

2. next step is to add the plugin to your vite configuration

```javascript
import resumable from "vite-plugin-resumable"

  ...
  plugins: [
    resumable();
  ]
  ...

```

By default, this plugin looks in `pages` folder for all files with .tsx extension and creates a server entry for each file found.

So go ahead and create a page called hello-world.tsx script in _pages_ folder

```javascript

// ~/pages/hello-world.ts
export function view() {
   console.log("Hello server");
   const counter = new State(1);
   counter.subscribe({
     next(value) { 
       console.log("counter", value);  
     }
   });

   function client() { 
      console.log("Hello client");
      document.body.addEventListener("click", 
          () => counter.set(x => x + 1));
   }

   return client;
}
```

The resumable package will automatically **infer** the client code from the result of the server entry function.
In this case the client code is generated from the function declaration of `client`. This function will be executed on client and will use the hydrated instance of the counter object, including the registered _next-observer_.


Let's start the application and see this in action
> npm start

Open a browser and navigate to _http://localhost:5173/hello-world_ 

```shell
// terminal output
Hello server
```

```shell
// browser output
Hello client
```

Clicking on the document will increment the counter and trigger the observer which will print current value to the console of your browser.


```
// browser output
counter, 2
```


## Conclusion
This plugin streamlines the development process by simplifying the code structure required to make a web application that can be executed on both the server and client side.

This is currently an alpha (aka proof of concept) version to verify usefulness of this approach. So far, this plugin captures the generic features of SSR so that any UI library could benefit from it.

We optimize the amount of client script that is sent to the client, but in the future we will be looking for more opportunities to optimize the state data (e.g. if we only access a property of an object then we may want to sent only the value of that property instead of the whole object.

next is to try this out with React and I will also build an adapter for [@xania/view](https://github.com/xania/view)