npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@nsrv/express-jsx-views

v0.0.2

Published

A React and Inferno template engine for Express

Downloads

18

Readme

express-jsx-views

An Express template engine for JSX-based views.

Licensed under 0BSD, which basically means you can use any code here freely for any purpose, without attribution requirements. Feel free to attribute if you want, and let me know if you use my code somewhere cool!

Installation

# Required
npm i @nsrv/express-jsx-views

# For Inferno
npm i inferno inferno-hyperscript inferno-server

# For React
npm i react react-dom

The necessary babel plugins are included for both.

Usage

For development, you will want to setup nodemon or whatever restarts your server on changes to watch the view files. With nodemon, that means adding the -e option like -e js,jsx,mjs,json to include .jsx files.

Strictly speaking, you don't have to use .jsx for your view files, if you set the view engine to 'js' instead, it will work fine. But it makes a good convention, and lets you easily know which files are probably being compiled through Babel Register.

Inferno example

index.js

'use strict';

const express = require('express');
const createRenderer = require('@nsrv/express-jsx-views');

const app = express();

app.set('views', 'views');
app.set('view engine', 'jsx');
app.engine('jsx', createRenderer('inferno'));

app.get('*', (req, res) => {
	res.locals = {
		title: 'Homepage',
	};
	res.render('home', { name: 'Sam' });
});

app.listen(3000);

views/home.jsx

'use strict';

function Home({ name, _locals }) {
	return (
		<html lang="en">
			<head>
				<meta charset="UTF-8" />
				<meta name="viewport" content="width=device-width, initial-scale=1.0" />
				<title>{_locals.title}</title>
			</head>
			<body>
				<h1>Hello {name}</h1>
			</body>
		</html>
	);
}

module.exports = Home;

React example

For React, just take the Inferno example and switch from

app.engine('jsx', createRenderer('inferno'));

to

app.engine('jsx', createRenderer('react'));

Also the charset prop on the first <meta> element needs to be renamed charSet for React.

Features

  • Simple React and Inferno default renderer setups.
  • Inferno default renderer passes the view props as props to the top component, but also into the global context.

You can get the global context in any function component as the second argument

// Inferno only
function MyComponent(props, context) {
  return <h1>{context._locals.title}</h1>;
}

A React equivalent is an eventually planned feature, and will probably utilize an external package exporting a React Context.

  • (Unstable) Checks that any passed view path is actually in one of the configured view folders. Unlike the Express default, you can't pass an arbitrary absolute path or traverse outside the views folders. res.render('../outside-file.jsx') will cause a throw.
    • This is for convenience and mistake catching, it's not thoroughly tested as a security measure. Don't pass arbitrary user input to res.render()'s first argument.

API

const createRenderer = require('@nsrv/express-jsx-views');

createRenderer(renderer: RendererOption, [options: Options])

RendererOption: 'inferno'|'react'|Function
// 'inferno' or 'react' will setup the basic renderers for each, with
// appropriate babel register transforms applied to the view folder
// automatically.
// A function lets you pass a custom renderer.

// A custom renderer has this signature
renderToString(Component, props)
// props will be the full Express template engine `options` object.

// A simple renderer example
const React = require('react');
const { renderToStaticMarkup } = require('react-dom/server');

function reactRenderer(Component, props) {
  return renderToStaticMarkup(React.createElement(Component, props));
}
// That's basically what the 'react' option does already. But this can be useful to add a custom element wrapper, such as a React Context Provider which you pass the props as value, to be used throughout the tree.

// Options
{
  // Universal options
  doctype: String, // default '<!DOCTYPE html>'
  // The doctype string will be prepended to all render outputs.
  // It's the only thing you simply *can't* do in JSX.
  // The default is correct (html5 and newer) for the vast majority of cases.

  // 'react' specific options
  jsxRuntime: 'automatic'|'classic', // default 'automatic'
  // By default we're using the modern JSX transform which does not require your source code to import React.
  // You can switch this to 'classic' to support older React.

  // Options when passing a custom renderer
  babelConfig: Object|Function|undefined, // default undefined
  // If you pass this option, Babel Register transform for view files will be
  // enabled, with the options given or options returned by calling a given
  // function `babelConfig(viewFolderPaths: Array)`. The viewFolderPaths
  // arguments will be `views` list Express passes to template engines.
  // example viewFolderPaths: ['/path/to/project/views']
  // The babel options will be merged with the BabelDefaults

  // 'inferno' doesn't have any extra options.
}

// BabelDefaults
{
  ignore: [],
  only: views,
  extensions: ['.jsx', '.js', '.mjs'],
  ...babelConfig, // or `...babelConfig(viewFolderPaths)`
}

// The default Inferno renderer babel config
{
	presets: [
		[
			'@babel/preset-env',
			{
				targets: {
					node: 'current',
				},
			},
		],
	],
	plugins: ['babel-plugin-inferno'],
}

// The default React renderer babel config
{
  presets: [
    [
      '@babel/preset-env',
      {
        targets: {
          node: 'current',
        },
      },
    ],
    [
      '@babel/preset-react',
      {
        runtime: jsxRuntime, // default 'automatic'
      },
    ],
  ],
}

Alternatives

Advantages over express-react-views

  • Supports Inferno
  • Supports custom rendering wrappers
  • More permissive license (0BSD), no patent grant stuff

However, express-react-views might be a better choice if these things are important to you

  • Older package, longer existing stability track record
  • Clearing the require cache without server restart in development
    • express-jsx-views outsources reloading by expecting your development process to use nodemon or similar, and have it watch the .jsx files.