Server-side render your web components.

Web component server-side rendering and testing

This repo contains all you need to server-side render your web components and run their tests in a node environment.

  • Uses undom for a minimal DOM API in Node.
  • Great for rendering out static sites from components.
  • Run your tests in Jest!
  • Statically generate JS files to HTML files.


npm install @skatejs/ssr


This example is using vanilla custom elements and shadow DOM in order to show that it can work with any web component library.

On the server (example.js):

const render = require('@skatejs/ssr');

class Hello extends HTMLElement {
  connectedCallback() {
    const shadowRoot = this.attachShadow({ mode: 'open' });
    shadowRoot.innerHTML =
      '<span>Hello, <x-yell><slot></slot></x-yell>!</span>';
class Yell extends HTMLElement {
  connectedCallback() {
    Promise.resolve().then(() => {
      const shadowRoot = this.attachShadow({ mode: 'open' });
      shadowRoot.innerHTML = '<strong><slot></slot></strong>';
customElements.define('x-hello', Hello);
customElements.define('x-yell', Yell);

const hello = new Hello();
hello.textContent = 'World';


And then just node your server code:

$ node example.js
<script>function __ssr(){var a=document.currentScript.previousElementSibling,b=a.firstElementChild;a.removeChild(b);for(var c=a.attachShadow({mode:"open"});b.hasChildNodes();)c.appendChild(b.firstChild);}</script><x-hello><shadow-root><span>Hello, <x-yell><shadow-root><strong><slot></slot></strong></shadow-root><slot></slot></x-yell><script>__ssr()</script>!</span></shadow-root>World</x-hello><script>__ssr()</script>

On the client, just inline your server-rendered string:

<script>function __ssr(){var a=document.currentScript.previousElementSibling,b=a.firstElementChild;a.removeChild(b);for(var c=a.attachShadow({mode:"open"});b.hasChildNodes();)c.appendChild(b.firstChild);}</script><x-hello><shadow-root><span>Hello, <x-yell><shadow-root><strong><slot></slot></strong></shadow-root><slot></slot></x-yell><script>__ssr()</script>!</span></shadow-root>World</x-hello><script>__ssr()</script>

See it in action!


The only function this library exposes is render(). The first argument is the DOM tree you want to render. It can be a document node or any HTML node. The second argument are the options to customise rendering. These options are:

  • debug - Whether or not to pretty print the HTML result. Defaults to false.
  • rehydrate - Whether or not to add the inline rehydration scripts. Defaults to true.
  • resolver - The function to call that will resolve the promise. Defaults to setTimeout.

Running in Node

If you want to run your code in Node, just require the registered environment before doing anything DOMish.

// index.js

// DOM stuff...

Running in Jest

If you want to run your tests in Jest, all you have to do is configure Jest to use the environment we've provided for it.

// package.json
  "jest": {
    "testEnvironment": "@skatejs/ssr/jest"

Static-site generation

This package ships with a command that you can use to statically generate a site from JS files.

  • It uses babel-register to parse your JS files.
  • Each JS file must have a default export that is a custom element.
ssr --out public --src path/to/site/**/*.js

Options are:

  • --babel - Path to custom babel config. Uses require() to load relative to process.cwd(). Defaults to .babelrc / package.json field.
  • --debug - Whether or not to pretty print the HTML result. Defaults to false.
  • --out - The directory to place the statically rendered files.
  • --props - A JSON object of custom props to assign to the custom elements before they're rendered.
  • --rehydrate - Whether or not to add rehydration scripts. Defaults to true.
  • --src - A glob for the source files to statically render to --out.
  • --suffix - The suffix to put on the output files. Defaults to html;

Watching files and generating them in dev mode

You can use something like nodemon to watch for updates and then regenerate your site:

nodemon --exec "ssr --out public --src path/to/site/**/*.js" --watch path/to/site

Running with other Node / DOM implementations

There's other implementations out there such as Domino and JSDOM. They don't yet have support for custom elements or shadow DOM, but if they did, then you would use this library in the same way, just without requiring @skatejs/ssr/register. With some implementations that don't yet support web components, requiring @skatejs/ssr/register may work, but your mileage may vary. Currently only Undom is officially supported.

The future

The definition of success for this library is if it can be made mostly redundant. Things like a DOM implementation in Node (JSDOM / UnDOM, etc) are still necessary. The static-site generation will probably still be a thing. However, we hope that the serialisation and rehydration of Shadow DOM can be spec'd - in some way - and a standardised API for doing so makes it's way to the platform.

Serialisation may still be done in a Node DOM implementation, but it'd be great to see it standardised beacuse it is tightly coupled to the rehydration step on the client. This also helps to ensure that if an imperative distrubution API ever makes its way into the spec, that both serialisation and rehydration may be accounted for.


There's some notes and limitations that you should be aware of.

Scoped styles

Scoped styles are emulated by scoping class names only. This means you are limited to using only class names within your shadow root <style /> tags:

  .some-class {}

It will make that class name unique and scope it to the shadow roots that use it.

Support for both :host and :slotted still need to be implemented.

Style tags are also deduped. This means that if you use a <style /> element that has the same content in several places, it will only be added to the head once. If you enable rehydration, it will pull from that script tag directly when attaching a shadow root.

DOM API limitations

You're limited to the subset of DOM methods available through Undom, plus what we add on top of it (which is quite a bit at the moment). Undom works well with Preact and SkateJS due to their mininmal overhead and limited native DOM interface usage.

There's currently some work happening to get custom element and shadow DOM support in JSDOM. Once that lands, we'll have broader API support and we can start thikning about focusing this API on just serialisation and rehydration.


  • Performance benchmarks focus on comparing a baseline to different methods of rehydration. Thanks to @robdodson for sharing some code that helped me flesh these out. Spin up a static server and load them up for more details.
  • Inline <script> tags use relative DOM accessors like document.currentScript, previousElementSibling and firstElementChild. Any HTML post-processing could affect the mileage of it, so beware.
  • Inline <script> method is currently the fastest overall method of rehydration. This has been discussed elsewhere but the difference between methods seemed more pronounced, possibly because things were deduped in a single <template> which isn't really possible because most components will be rendered in a different state. Also, cralers don't read content in <template> elements, so we need to store it in non-inert blocks.
  • Using a custom <shadow-root> element seems acceptable for performance, however there's some problems with delivering it:
    • Do we ship an ES5 or ES6 component? ES5 requires transpilation and shims. ES6 excludes older browsers.
    • We could make the consumer ship the element themselves and provide helpers they call out to, but that's more friction.
    • This is probably a better method once we can assume custom elements / ES2015 support in all targeted browsers.
  • Shadow root content, prior to being hydrated, is not inert so that it can be found by querySelector and crawlers. Putting it inside of a <template> tag means that it's not participating in the document and the aforementioned wouldn't work, thus negating the benefits of SSR altogether.
  • Using invalid HTML, such as putting a <div /> in a <p /> tag could result in broken rehydration because the browser may try and "fix" the incorrect line, thus making things out of sync with what the rehydration script expects.







  • 针对搜索引擎爬虫的欺骗式ssr

    玩Google Webmasters的可能会有这种经历。自己开发的app用了Vue/React,写完后用Fetch as Google一爬傻眼了,爬不到东西。 网上搜解决方案出来的都是一堆额外的SS...

    3 年前
  • 针对搜索引擎爬虫的欺骗式SSR

    玩Google Webmasters的可能会有这种经历。自己开发的app用了Vue/React,写完后用Fetch as Google一爬傻眼了,爬不到东西。 网上搜解决方案出来的都是一堆额外的SS...

    3 年前
  • 这是一个基于 Vue SSR Genesis 框架快速开发的模板例子

    介绍 这是一个基于 Vue SSR Genesis 框架快速开发的模板例子 启动 # 安装依赖 npm install # 开发 npm run dev # 编译 npm run build # ...

    8 个月前
  • 跟著小明一起搞懂技術名詞:MVC、SPA 與 SSR

    這篇的靈感來自於 Front-End Developers Taiwan 裡面的一串討論,有人 po 了一個影片是來討論「MVC vs SPA」,這個標題一出來大家都驚呆了,想說怎麼會有這樣的比較,...

    2 年前
  • 超简单的react服务器渲染(ssr)入坑指南

    前言 本文是基于react ssr的入门教程,在实际项目中使用还需要做更多的配置和优化,比较适合第一次尝试react ssr的小伙伴们。技术涉及到 koa2 + react,案例使用create-re...

    2 年前
  • 记一次惨痛的 Vue SSR 内存泄漏排查

    背景 近期,对一个老项目进行直出改造,将其从 Vue 客户端渲染改造成支持 Vue 服务端渲染(SSR),然而在测试环境进行压测的过程中,就发现其存在明显的内存泄漏问题。

    5 个月前
  • 解密Vue SSR

    作者:百度外卖 耿彩丽 李宗原 转载请标明出处 引言 最近笔者和小伙伴在研究Vue SSR,但是市面上充斥了太多的从0到1的文章,对大家理解这其中的原理帮助并不是很大,因此,本文将从Vue SSR...

    3 年前
  • 编写支持SSR通用组件指南


    2 年前
  • 细说后端模板渲染、客户端渲染、node 中间层、服务器端渲染(ssr)

    前端与后端渲染方式的发展大致经历了这样几个阶段:后端模板渲染、客户端渲染、node 中间层、服务器端渲染(ssr)。 1. 后端模板渲染 前端与后端最初的渲染方式是后端模板渲染,就是由后端使用模板引擎...

    2 年前
  • 简述SSR服务端渲染之使用Nuxt搭建一个vue从0到1的项目!

    导读 vue的高效开发不言而喻,但是单纯的使用vue-cli开发是否经常遇到页面不被收录,爬虫抓取不到页面的苦恼呢?那我们一起来看看nuxt-vuejs的通用框架。

    4 个月前


扫码加入 JavaScript 社区