Server side rendering of JSX / React like elements and pure functional components into HTML.
const oj = require("oj");
oj.render.toString(
<Document title="JSX with OJ">
<h1>JSX with OJ</h1>
<p>In Node</p>
</Document>
);
function Document(props) {
return <>
{"<!doctype html>"}
<title>{props.title}</title>
{props.children}
</>;
}Only a small portion of the React API needed for templating is implemented.
createElementFragmentcreateContextuseContext
Render an element and its children.
renderFunction(element, context = {})element- Result ofcreateElementcontext- A mutable object passed into all functional components
See notes on Simple contexts further down
Returns a promise resolving with an HTML string
Returns a readable stream of HTML element parts and content
Returns a readable stream of objects describing HTML element parts and content
{ name, attrs }- An open tag{ name }- A close tag{ text }- A text node
Transform contents of a rendering stream
renderingStream.pipe(transformFunction())Returns a transform stream transforming objects from toObjectStream into HTML element parts and content
Compile JSX templates with Babel and a React setup. Use the following configuration for the transform react jsx plugin:
{
"pragma": "oj.createElement",
"pragmaFrag": "oj.Fragment"
}npm install oj @babel/core @babel/register @babel/preset-react eslint eslint-plugin-reactrequire("./setupViews.js");
const oj = require("oj");
const Template = require("./template.jsx");
const element = oj.createElement(Template, {props: "here"});
const html = await oj.render.toString(element, {context: "here"});// setupViews.js
require("@babel/register")({
extensions: [".jsx"],
presets: [
["@babel/preset-react", {
pragma: "oj.createElement",
pragmaFrag: "oj.Fragment"
}]
]
});// template.jsx
const oj = require("oj");
module.exports = function Template(props) {
return <h1>{props.title}</h1>;
};// eslintrc.js
{
extends: ["plugin:react/recommended"],
settings: {
react: {
pragma: "oj"
}
},
rules: {
"react/prop-types": "off",
"react/jsx-key": "off",
"react/no-unknown-property": "off"
}
}Apart from very limited support, see API, there some differences.
React DOM uses camelCase property naming convention instead of HTML attribute names. OJ doesn't. Attributes are output as authored.
<p class="preamble">...</p>
<label for="search-field">Search</label>
<input id="search-field" type="search" data-attr="value" />
<div className="no-effect">className is not an attribute</div>For Eslint you might want to turn off the rule
react/no-unknown-property
When rendering lists React uses the key attribute is used for efficient diffing when updating a component. OJ only renders markup server side, so there is no need for this.
For Eslint you might want to turn off the rule
react/jsx-key
None whatsoever.
For Eslint you might want to turn off the rule
react/prop-types
Currently there are no escaping mechanisms for anything. Content is output as authored.
<>
{"<!doctype html>"}
<div>
{"<p>html content</p>"}
</div>
</><!doctype html>
<div>
<p>html content</p>
</div>*Whitespace for readability
All functional components are passed an additional argument, apart from props, context. It is a mutable object. Initial value is derived from the second argument of any rendering function.
const oj = require("oj");
oj.render.toString(<Recursive />, { prop: 1 });
function Recursive(props, context) {
if (context.props > 3) return;
return <>
<i>{context.props++}</i>
<Recursive />
</>;
}<i>1</i><i>2</i><i>3</i>Although the syntax is the same the behavior is different.
React rendering is synchronous. OJ rendering is asynchronous.
In React a context consumer will retrieve the value from the closest provider in the document tree. In OJ contexts are unaware of the document tree. A provider will push its value into a list, render its children then pop the list. A consumer will get the latest value available.
This means contexts existing independent of the rendering cycle could share its values with providers from parallel cycles.
Attach contexts to the rendering cycle context object. It is passed to all functional components.
const oj = require("oj");
function ParentComponent(props, context) {
const SomeContext = oj.createContext();
context.SomeContext = SomeContext;
return <>
<SomeContext.Provider value={1}>
<DeepComponent />
</SomeContext.Provider>
</>;
}
function DeepComponent(props, context) {
const value = oj.useContext(context.SomeContext);
return <div>{value}</div>
}Probably a couple?
- vhtml - Renders markup inside out which makes contexts and avoiding unwanted escaping impossible(?)