/genz

Streaming-first SSR for Node Apps

Primary LanguageJavaScriptMIT LicenseMIT

Gen Z

Streaming-first SSR for Node Apps.

⚠️ WARNING: This is experimental.

Contents

Installation

$ npm install genz

Quick Example

import http from 'http';
import { _, toStream } from 'genz';

http.createServer((req, res) => {
  if (req.url !== '/') return res.end();

  const content = _.html(
    _.head(
      _.title('Basic Example'),
      _.style(css('body', {
        backgroundColor: 'yellow'
      }))
    ),
    _.body(
      _.h1('Hello World'),
      _.p('This is a basic example.')
    )
  );
  
  res.writeHead(200, {
    'Content-Type': 'text/html',
    'Transfer-Encoding': 'chunked'
  });
  
  toStream(res, content);

}).listen(3000, () => {
  console.log('http://localhost:3000');
});

For a more complex example checkout this example.

To explore this example, clone this repo and run:

$ cd path/to/cloned/repo
$ npm i
$ npm run dev

Creating HTML

Genz uses conventions familiar to people familiar with the h function beneath jsx, but with some slight ergonomic changes. so to create content you can do the following:

import { _ } from 'genz';

_.div({ class: 'my-class' },
  _.p('Hello there!')
);

...translates to:

<div class="my-class">
  <p>
    Hello there!
  </p>
</div>

Note: Unlike most h functions, genz attaches every tag to the _ object (since on the server we don't have to be as precious about package size). This avoids importing tags one by one, which is a pain, and it makes reading the templates a bit easier.

You can pass children as an array, a nested array, as further arguments, or any mixture. So the following all work:

_.div('This is a sentance');
_.div('This ', 'is ', 'a ', 'sentance');
_.div([ 'This ', 'is ', 'a ', 'sentance.' ]);
_.div(['This ', ['is ', 'a ']], 'sentance.');

If you want attributes on a tag you must pass them as the first argument:

_.section({ id: 'my-id', class: 'my-class' }, /* any children... */)

👀 Play with templating in the playground


Rendering HTML

So far we have only produced a data object that can be sent to a writable stream. Things get a bit more interesting when we render these objects. First, though, let’s take a look at rendering strings.

To a String

Needs documentation...

To a Writable Stream

Needs documentation...

Using Promises

Needs documentation...

Providing a Context

Needs documentation...

Consuming Readable Streams

Needs documentation...


Error Handling

Needs documentation...

Extras

Inline CSS

Needs documentation...

Deduping

Needs documentation...

Deduping experiment

Creating a Custom Tag

If you want to create a tag before you make templates you can do this:

import { createTag } from 'genz';

const myTag = createTag('my-tag'); // myTag({ id: 'nice' }, 'hi') => <my-tag id="nice">hi</my-tag>
const myVoidTag = createTag('my-void-tag', true); // myVoidTag({ id: 'nice' }) => <my-void-tag id="nice">