Use JSX without React
NakedJSX is a command-line tool for generating HTML files from JSX. The output is pure HTML and CSS - unless you choose to add your own JavaScript.
This is an overview. Please refer to the documentation for a detailed look at each feature.
This page was built using NakedJSX. You can look at its source.
At a Glance
Generate static HTML files from JSX by running an npx command.
Scoped CSS classes are extracted from JSX and deduplicated.
No need to set up a Node.js project, just run an npx command on your NakedJSX content directory and your site is built into an output folder.
A development mode with a live-refresh build and web server is included.
NakedJSX provides an optional and small runtime allowing JSX to be used by client-side JavaScript. The runtime is automatically injected if needed, and adds around half a kilobyte to the file.
A Two Minute Test
If you have Node.js installed, you can try NakedJSX right now. Create a directory called src
with the following file (the filename must end in -page.jsx
):
src/index-page.jsx
import { Page } from '@nakedjsx/core/page'
const BodyContent =
({ title }) =>
<>
<h1 css="color: fuchsia">{title}</h1>
<p css="color: #ff00ff">
Building HTML files from JSX feels right.
</p>
</>
Page.Create('en');
Page.AppendCss('body { font-family: sans-serif }');
Page.AppendHead(<title>Hello NakedJSX</title>);
Page.AppendBody(<BodyContent title="Hello NakedJSX" />);
Page.Render();
Then open the parent directory in your terminal and run:
# build command
$ npx nakedjsx src --out out --pretty
The result is a new subdirectory called out
, which contains a single HTML file:
out/index.html (401 bytes) (open in new tab)
<!doctype html>
<html lang="en">
<head>
<title>Hello NakedJSX</title>
<style>
body {
font-family: sans-serif;
}
.a {
color: #f0f;
}
</style>
</head>
<body>
<h1 class="a">Hello NakedJSX</h1>
<p class="a">Building HTML files from JSX feels right.</p>
</body>
</html>
Note that the scoped CSS was extracted from the JSX, minified, and then deduplicated.
If you build it without the --pretty
flag, the result is tightly packed and suitable for distribution:
out/index.html (241 bytes) (open in new tab)
<!DOCTYPE html><html lang="en"><head><title>Hello NakedJSX</title><style>body{font-family:sans-serif}.a{color:#f0f}</style></head><body><h1 class="a">Hello NakedJSX</h1><p class="a">Building HTML files from JSX feels right.</p></body></html>
If you build with the --dev
flag then a live-refresh development webserver will be started.
Adding Client JavaScript
You can also add client JavaScript that will run in the browser when the page loads. Client JavaScript can also use JSX.
Here's the previous example converted to add the same <BodyContent>
in client JavaScript:
src/index-page.jsx
import { Page } from '@nakedjsx/core/page'
const BodyContent =
({ title }) =>
<>
<h1 css="color: fuchsia">{title}</h1>
<p css="color: #ff00ff">
You might not need a single page app?
</p>
</>
// Setup the static HTML, without adding <BodyContent>
Page.Create('en');
Page.AppendCss('body { font-family: sans-serif }');
Page.AppendHead(<title>Hello NakedJSX</title>);
// Make the BodyContent JSX function available to browser JavaScript
Page.AppendJs(BodyContent);
// Add some code that will run when a browser loads the page
Page.AppendJs(document.body.appendChild(<BodyContent title="Hello NakedJSX" />));
Page.Render();
The output contains a small amount JavaScript to support creating DOM nodes from JSX, the compiled <BodyContent>
function, and the code that adds it to the document (all minified):
out/index.html (2034 bytes) (open in new tab)
<!doctype html>
<html lang="en">
<head>
<title>Hello NakedJSX</title>
<style>
body {
font-family: sans-serif;
}
.a {
color: #f0f;
}
</style>
</head>
<body>
<script>
'use strict'
const t = Element.prototype.appendChild
function e(t, e, ...n) {
if (((e = e || {}), 'function' == typeof t))
return (e.children = n), t(e)
const o = document.createElement(t)
for (const [t, n] of Object.entries(e)) {
if (t.startsWith('on')) {
const e = t.toLowerCase()
if (e in window) {
o.addEventListener(e.substring(2), n)
continue
}
}
o.setAttribute(t, n)
}
for (const t of n) o.appendChild(t)
return o
}
;(Element.prototype.appendChild = function (e) {
if (Array.isArray(e)) {
for (const t of e) this.appendChild(t)
return e
}
return 'string' == typeof e
? t.call(this, document.createTextNode(e))
: e
? t.call(this, e)
: void 0
}),
document.body.appendChild(
e(
({ title: t }) => [
e('h1', { class: 'a' }, t),
e(
'p',
{ class: 'a' },
'You might not need a single page app?',
),
],
{ title: 'Hello NakedJSX' },
),
)
</script>
</body>
</html>
Without pretty printing, the result is less than a single kilobyte in size:
out/index.html (891 bytes) (open in new tab)
<!DOCTYPE html><html lang="en"><head><title>Hello NakedJSX</title><style>body{font-family:sans-serif}.a{color:#f0f}</style></head><body><script>"use strict";const t=Element.prototype.appendChild;function e(t,e,...n){if(e=e||{},"function"==typeof t)return e.children=n,t(e);const o=document.createElement(t);for(const[t,n]of Object.entries(e)){if(t.startsWith("on")){const e=t.toLowerCase();if(e in window){o.addEventListener(e.substring(2),n);continue}}o.setAttribute(t,n)}for(const t of n)o.appendChild(t);return o}Element.prototype.appendChild=function(e){if(Array.isArray(e)){for(const t of e)this.appendChild(t);return e}return"string"==typeof e?t.call(this,document.createTextNode(e)):e?t.call(this,e):void 0},document.body.appendChild(e((({title:t})=>[e("h1",{class:"a"},t),e("p",{class:"a"},"You might not need a single page app?")]),{title:"Hello NakedJSX"}));</script></body></html>
Several other ways to add client JavaScript are covered in the documentation.
Features
- Build HTML files using JSX templates
- Live-refresh development server
- Template engine for Express and other HTTP servers
- TypeScript support (experimental)
- JSX support for 'ref' and 'children' props
- JSX parent and child communication via 'context' prop
- Scoped CSS extraction from JSX
- CSS nesting
- CSS minification and deduplication
- Publish images and other asset files
- Generate image sourcesets at multiple resolutions
- Convert images to webp
- Embed raw asset files in HTML (useful for SVG)
- Client JavaScript minification
- Use JSX in client JavaScript
- Embed predefined values at build time
- Configurable import path aliases
- Asset import plugin system
You can find detailed information in the documentation.
Project Status
NakedJSX is approaching version 1.0. Feedback is very welcome!
Please join us on the NakedJSX Discord Server or get in touch via contact@nakedjsx.org.
Design Philosophy
Pure HTML & CSS Output
The output files are ready for your browser to render without executing any JavaScript.
Of course, you can add any client JavaScript you like, and if NakedJSX builds that code then it will be able to use inline JSX to create DOM nodes.
Low-Friction
NakedJSX doesn't require the setting up and maintaining of a Node.js project. Just create the site files and run the npx command to build.
API Stability (After 1.0 Release)
Sites should continue to build with newer versions of NakedJSX.
Conservative Dependency Choices
Where practical, required functionality is implemented directly within NakedJSX. In other cases, well known external dependencies are chosen.
Security Compliance
Supported versions of NakedJSX will aim for an ongoing state of zero npm audit findings.
Dependencies & Acknowledgments
NakedJSX relies on these projects for core functionality. Please consider supporting them.
Babel(donate)
- Transpilation of JSX to JavaScript
- Hosting several NakedJSX plugins
* Plus @babel/generator, @babel/plugin-syntax-jsx, @babel/plugin-transform-react-jsx plugins, and the @babel/preset-env preset.
Chokidar(donate)
- Notification of source file changes
github.com/paulmillr/chokidar#readme
CSSO
- CSS optmisation / compression
CSSTree
- CSS manipulation
github.com/csstree/csstree#readme
js-beautify
- Pretty printing of HTML, CSS and JavaScript code
github.com/beautify-web/js-beautify#readme
Node.js
- JavaScript runtime environment and API
PostCSS(donate)
- Transpilation of nested CSS into flat CSS
* Plus postcss-nested plugin.
React
NakedJSX does not use React, but the excellent ideas and work of the engineers at Meta (who created JSX!) must be acknowledged.Rollup
- Production of self-contained JavaScript bundles
- Hosting NakedJSX plugins
* Plus @rollup/plugin-babel.
Terser
- JavaScript optmisation / compression
Project Owner
Hello, I'm David Hogan. Since 1999 I have been a professional software engineer, startup/scaleup CTO, and everything in between. I am currently the CTO at Cortical Labs and a member of the VICE (Commodore 8-bit computer emulator) development team.
I designed and built NakedJSX by following my curiosity and I hope that you find it useful.
You can reach me at: