When you want to generate html on the server you will need some template engine.
You can use, EJS, handlebars or many others, but I prefer a template engine where I can create components instead of working with partials and layouts.
Luckily Deno has built-in support for JSX thanks to swc. JSX is a syntax extension to Javascript. This means that JSX will be translated to real javascript calls like React.createElement() (more on this below). This is nice, but Deno doesn't know about React so we need to do some more work.
First we will create a simple Deno application to get started.
Put this code in main.js
:
import { serve } from "https://deno.land/std@0.92.0/http/server.ts";
const server = serve({ port: 8000 });
const headers = new Headers();
headers.append("Content-Type", "text/html; charset=UTF-8");
for await (const req of server) {
req.respond({
status: 200,
headers,
body: `
<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
<link
href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css"
rel="stylesheet">
</head>
<body>
<h1 class="text-3xl m-2">Hello world</h1>
<button
class="border bg-indigo-600 text-white px-2 py-1 rounded m-2">
Useless button
</button>
</body>
</html>`,
});
}
You can start this with deno run --allow-net ./main.js
. Now you can open your browser on localhost:8000
and view the html page.
The goal is to replace this html template string with JSX components.
React 16
It's time to create our first component pages/home.jsx
.
For now this is a single component with the complete html.
import React from "https://jspm.dev/react@16.14.0";
export default function () {
return (
<html>
<head>
<title>Hello</title>
<link
href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css"
rel="stylesheet"
/>
</head>
<body>
<h1 className="text-3xl m-2">Hello world</h1>
<button
className="border bg-indigo-600 text-white px-2 py-1 rounded m-2"
>
Useless button
</button>
</body>
</html>
);
}
Now it's time to use this component in main.js
import { serve } from "https://deno.land/std@0.92.0/http/server.ts";
import ReactDOMServer from 'https://jspm.dev/react-dom@16.14.0/server';
import home from "./pages/home.jsx"
function render(jsx) {
return ReactDOMServer.renderToString(jsx());
}
const server = serve({ port: 8000 });
const headers = new Headers();
headers.append("Content-Type", "text/html; charset=UTF-8");
for await (const req of server) {
req.respond({
status: 200,
headers: headers,
body: render(home),
});
}
This includes a new render function that executes the JSX function and renders the result to a string.
Instead of renderToString you can also use renderToStaticMarkup.
We now have a working JSX example with React 16!
React 17 is at the moment of writing not yet supported. The problem is that in React 17 the JSX is translated to something new. It isn't React.createElement anymore to avoid the need for importing React.
I first tried to load React 16 from Skypack CDN, but that doesn't work because of this issue
Preact
It is also possible to use Preact instead of React.
Since JSX is translated to React.createElement() we have to replace React
with some other class.
There are 2 ways of doing this.
- with a JSX pragma
- with tsconfig.json
/** @jsx h */
import { h } from "https://cdn.skypack.dev/preact";
The first line is the JSX pragma. This means to use h
instead of React
.
But you can also use tsconfig.json
so you don't need the pragma everywhere.
You have to run Deno with the link to the config deno run --config ./tsconfig.json ...
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "h",
}
}
The render function in main.js
looks like this:
import renderToString from "https://cdn.skypack.dev/preact-render-to-string@v5.1.12";
function render(jsx) {
return renderToString(jsx());
}
Result
In the final version I created extra components for the layout and for the button.
pages/home.jsx
import React from "https://jspm.dev/react@16.14.0";
import Layout from "../components/layout.jsx";
import Button from "../components/button.jsx";
export default function () {
return (
<Layout title="Hello">
<h1 className="text-3xl m-2">Hello world</h1>
<Button>
Useless button
</Button>
</Layout>
);
}
components/button.jsx
import React from "https://jspm.dev/react@16.14.0";
export default function ({ children }) {
return (<button
className="border bg-indigo-600 text-white px-2 py-1 rounded m-2"
>
{children}
</button>);
}
components/layout.jsx
import React from "https://jspm.dev/react@16.14.0";
export default function ({ children, title }) {
return (
<html>
<head>
<title>{title}</title>
<link
href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css"
rel="stylesheet"
/>
</head>
<body>
{children}
</body>
</html>
);
}
I hope this will get you started with JSX in Deno. This is just a simple example and there is a lot to improve like using deps.ts
and Typescript. But I try to keep this example focussed on JSX.
You can find all the code here.
Top comments (0)