Home

Awesome

ts-liveview

Build hybrid SSG and SSR realtime SPA/MPA with Typescript

ts-liveview helps to deliver fast and interactive user interface directly from node.js server.

(Without MBs of javascript to be downloaded and executed on the client side.)

The client-side runtime of ts-liveview is below 13KB (2.3KB bundled, minified and gzipped).

ts-liveview supports JSX but it doesn't rely on Virtual DOM. Instead, precise DOM operations are derived from application-specific event handlers, and sent to the browser client(s) for realtime UI updates.

中文版本

Get Started

To create a new project, run:

## start from a template
npm init ts-liveview my-app
# or "npx create-ts-liveview@latest my-app" for latest version

cd my-app

## install packages and setup sqlite database
./scripts/init.sh

## starts the development server
npm start

To setup a cloned project, run ./scripts/init.sh, which will install packages and setup sqlite database for you.

To update database schema, see db/README.md

To deploy, setup scripts/config then run ./scripts/deploy.sh, which will build and deploy the server with knex migrate and pm2.

To test https-required functions during development, run ./scripts/caddy-run.sh, which will start a https reverse proxy. You can install caddy with ./scripts/caddy-install.sh on Mac or Linux, or ./scripts/caddy-install.ps1 on Windows.

Details refer to create-ts-liveview

Available npm scripts

npm start: Start the development server, with realtime-update and live-reload.

npm run build: Compile the typescript server into 'dist' folder, and bundle the client into 'build' folder. This step is only needed when preparing production deployment.

npm run fix: Auto add .js extension in import paths, which is required in esm runtime.

npm run format: Auto format the source code with prettier.

npm run lint: Lint the codebase with eslint and apply auto fix if possible.

npm run size: Build the frontend and check the size of bundled, minified, and gzipped versions respectively.

Features

Remarks:

<span id='feature-1'>[1]</span> Pay the AST-to-HTML conversion time-cost once at boot-time instead of at each request-time

<span id='feature-2'>[2]</span> Response contentful html page directly to GET request. Content chunk is streamed to clients as soon as it's ready, without waiting for client-side javascript bundles nor data requests to start rendering.

<span id='feature-3'>[3]</span> Updates can be triggered by (bi-directional) events from the server or other clients

<span id='feature-4'>[4]</span> With history.pushState() and PopStateEvent

<span id='feature-5'>[5]</span> For screen-reader, text-based browser, and people with slow or unstable network, or simply tried with privacy invading scripts

<span id='feature-6'>[6]</span> Each function component are evaluated with error handling, this approach can deliver as much as possible, avoiding blank pages when error occur.

<span id='feature-7'>[7]</span> The network client code is 0.4K to 0.9K minified, 102x to 45x smaller than socket.io.min.js

<span id='feature-8'>[8]</span> The entry point of ts-liveview app can be wrapped as an express middleware

<span id='feature-9'>[9]</span> ts-liveview is provided as a template (rather than a library), hence any part can be modified to suit your need

Size Comparison with other tools

ToolsRuntime Code Size (minified)
Vanilla0.3K
ts-liveview 46.5K OR same size as vanilla
Stencil 2.0.113.7K
Svelte 3.0.017.4K
Vue 3.2.3349.3K
React 17.0.2144.6K
Angular 13.3.0155.8K

Remark: Size of other tools taking reference from https://github.com/beenotung/spa-state-demo

Q & A

Why server-rendered?

<span id='html-streaming'></span>

Why HTML Streaming?

HTML Streaming enables progressive rendering. The server sends html chunks as soon as they're ready. The browser, on the other side, progressively receives and renders the content. As a result, the content will be visible to users earlier.

Below simulation from marko illustrates the visual difference with/without html streaming. In the example, both sides take the same amount of time to finish rendering. However, the right-hand-side example using html streaming shows the contents progressively, allowing the user to start reading earlier.

<figure> <p> <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/strm6tlc0vcjc5xzwcbu.gif" alt="Buffered pages don’t show content as it loads, but streaming pages show content incrementally." loading="lazy"> </p> <figcaption> <p> Illustration captured by <a href="https://dev.to/tigt/the-weirdly-obscure-art-of-streamed-html-4gc2">Taylor Hunt</a> from <a href="https://markojs.com/#streaming">markojs.com/#streaming</a> </p> </figcaption> </figure>

Despite the response body is sent with streaming, the document title and meta description can be generated dynamically according to routing result. Details see the Thermostat and routing demo.

<span id='jsx'></span>

Why JSX?

Previous versions of ts-liveview use template string to build html. It allows the engine to quickly construct the html output for morphdom to patch the DOM.

With the template string based approach, html injection (XSS attack) could be avoided when explicitly using helper function to sanitize the dynamic content. However it requires the developer to be careful, which could be bug-prone.

Using JSX, the string values are auto escaped, which helps to prevent XSS from dynamic content.

Why not using html-based template language?

One may find it more productive to work with html-based template language like ember/angular/vue/svelte. However, interpreting/compiling a DSL with looping ability, conditional branching, scoped variables, reusability and testability into low-level code is rather complex.

Instead, ts-liveview adopts a data structure DSL (dsDSL) built on the primitive data structures provided from the programming language, such as string, array, object and function.

A thin abstraction layer is created to improve development experience (DX).

This article from Federico explains the pros and cons of declarative programming and dsDSL in detail.

<span id="no-vdom-diff"></span>

Why no virtual-dom diff?

The current implementation of ts-liveview updates the DOM using explicit css selector (aka document querySelector). This design reduces the memory requirement on the server to better support simultaneous connections.

The application can be built on top of reactive model powered by S.js, RxJS, or OOP with getter and setter.

Example using getter and setter see thermostat.tsx

Template of common use cases

Examples / Demo

Examples to be done:

Inspired from

Releases

Details refers to Changelog

License

This project is licensed with BSD-2-Clause

This is free, libre, and open-source software (FLOSS). It comes down to four essential freedoms [ref]: