koa-ve v1.0.1
Vé
Asynchronous server side rendering of JSX using Koa
React's JSX is a pleasant enough way to write HTML, but when rendering server side, the whole shebang with states and JS-style attributes is needlessly complicated. Vé, named after the norse god who gave the original stick-figure-humans shapes and senses, is a small project to give simple JSX-support through Koa without distraction.
npm install --save koa-ve
Configured for Koa
The renderer is written with Koa's ctx
and next
in mind. While technically not a requirement, the alternative is to write your own middleware using Ve.engine
, which takes a writable stream and the component to render as its first two arguments. All later arguments will be available to the component-functions.
npm install --save koa
Better with Babel and its React/JSX transformation plugin
npm install --save-dev babel-cli babel-plugin-transform-react-jsx
The difference between
<div class="my-div">hello world</div>
and
Ve.component("div", {class: "my-div"}, "hello world");
is quite noticable, after all.
Configure .babelrc
{
"plugins": [
["transform-react-jsx",
{
"pragma": "Ve.component"
}
]
],
}
Use as middleware
app.use(require('koa-ve').render)
Write HTML components with JSX
All attribute names are written in the usual HTML-way, using double quotes, with the value's double-quotes escaped. There is white-list of valid attribute names in Ve.config
taken from MDN which determines what will be visible, also allowing /^data-[\w-]*$/
and /^on\w+$/
.
The configuration object also includes lists of tag-names for empty elements (br
, input
, etc) which do not display its children, and preformatted elements (textarea
, pre
) which filters away non-string children.
const Ve = require('koa-ve');
async function MyDiv (ctx) {
return <div {...this.attr} class={"my-div " + this.attr.class}>
{this.children}
</div>
}
All together
The following example can be found as small-demo/demo.jsx
.
const Koa = require('koa');
const Ve = require('koa-ve');
async function Component (ctx) {
ctx.state.title = "Component - " + ctx.state.title;
await new Promise((ok) => setTimeout(ok, 100))
return (
<div class="component">
<header>
<h1>
Hello World
</h1>
</header>
<main data-name={'"you"'} custom-attributes="will be hidden by default">
<Child name={"you"}>
Hello
</Child>
</main>
<footer>
© 2018
</footer>
</div>
)
}
function Child () {
return <h3>
{this.children}
to
<i>
{this.attr.name}
</i>
as well
</h3>
}
const app = new Koa();
app.use(Ve.render);
app.use(async (ctx, next) => {
ctx.state.title = "Vé"
let children = await next();
return (
<html>
<head>
<meta charset="utf-8"/>
<title>
{ctx.state.title}
</title>
<style>
{`
.component {
width: 250px;
margin: 50px;
}
h1 {
color: steelblue;
font-weight: normal;
}
`}
</style>
</head>
<body>
{children}
</body>
</html>
)
})
app.use(Component);
app.listen(3000);
And finally the source, as available to the browser:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>
Component - Vé
</title>
<style>
.component {
width: 250px;
margin: 50px;
}
h1 {
color: steelblue;
font-weight: normal;
}
</style>
</head>
<body>
<div class="component">
<header>
<h1>
Hello World
</h1>
</header>
<main data-name=""you"">
<h3>
Hello
to
<i>
you
</i>
as well
</h3>
</main>
<footer>
© 2018
</footer>
</div>
</body>
</html>