/hydron

SSR for Web Components

MIT LicenseMIT


SSR for Web Components

// my-component.js
import { mod } from 'hydron'

//
// hydron actually doesn't care about components, except
// for using a serializer that properly supports declarative shadow dom.
// instead it works with "modifications" to given `window` objects.
//
export default mod(window => {
  class MyComponent extends window.HTMLElement {
    // ...
  }

  window.customElements.define('my-component', MyComponent)
})
// index.js
import { Page } from 'hydron/server'
import myComp from './my-component'

const page = new Page()
page.use(myComp)

page.document.body.innerHTML = '<my-component></my-component>'
await page.save('dist/index.html')

WORK IN PROGRESS

My current idea of implementing this is packen + Happy DOM with some convenience wrappers over the APIs provided by the two libraries. This means the solution will be dependent on declarative shadow DOM which is yet to be supported by all major browsers (currently its Chrome and Edge). There are possible work-arounds for this limitation, for example forgoing encapsulation and inlining components that can be inlined, but I feel those will fall out of the scope of this project, although it should play nice with them.


Some usage examples:

// server-only modifications
import { mod } from 'hydron'

export default mod(window => ...)
// dependency between mods (e.g. components needing each other)
import { mod, use } from 'hydron'
import OtherMod from './other-mod'

export default mod(window => {
  use(OtherMod)
  // ...
})
// add a modification to client
import { mod, packMe } from 'hydron'

export default mod(window => {
  packMe()
  // ...
})
// wait for some tasks to be finished
import { mod, waitFor } from 'hydron'

export default mod(window => {
  waitFor(async () => {
    await someStuff()
    // ...
  })

  // ...
})
// wait for some task from within a component
import { mod, waitFor } from 'hydron'

export default mod(window => {
  class MyElemenet extends window.HTMLElement {
    connectedCallback() {
      waitFor(async() => {
        const res = await someData()
        // ...
      })

      // ...
    }
  }

  window.customElements.define('my-element', MyElement)
})