Rendering React SSR with Deno and Deno Deploy

Outline

Goals

Prerequisites

curl -fsSL https://deno.land/x/install/install.sh | sh# ORiwr https://deno.land/x/install/install.ps1 -useb | iex
deno install --allow-read --allow-write --allow-env --allow-net --allow-run --no-check -r -f https://deno.land/x/deploy@0.3.0/deployctl.ts

Create the Packages collection

npx create-strapi-app packages --quickstart

Building the React server

/// <reference path="https://raw.githubusercontent.com/denoland/deployctl/main/types/deploy.fetchevent.d.ts" />
/// <reference path="https://raw.githubusercontent.com/denoland/deployctl/main/types/deploy.window.d.ts" />
import * as React from "https://esm.sh/react@17.0.2";
import * as ReactDOMServer from "https://esm.sh/react-dom@17.0.2/server";
import { createElement as h } from "https://esm.sh/react@17.0.2";// @ts-ignore Because VSCode occasionally complains that Deno is not defined.
const STRAPI_API_URL = Deno.env.get("STRAPI_API_URL") ||
"http://localhost:1337";
interface Package {
name: string;
description: string;
github: string;
stars: number;
}
interface Props {
packages: Package[];
}
function App({ packages }: Props) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
crossOrigin="anonymous"
/>
<title>Hello from JSX</title>
</head>
<body>
<div className="container">
<h1>Hello, World!</h1>
<table className="table">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Description</th>
<th scope="col">Stars</th>
<th scope="col">URL</th>
</tr>
</thead>
<tbody>
{packages.map((pkg: Package) => (
<tr>
<th scope="row">{pkg.name}</th>
<td>{pkg.description}</td>
<td>{pkg.stars}</td>
<td>
<a href={pkg.github}>{pkg.github}</a>
</td>
</tr>
))}
</tbody>
</table>
</div>
</body>
</html>
);
}
async function getData(path: string) {
const url = `${STRAPI_API_URL}${path}`;
const response = await fetch(url, {
headers: {
"Content-Type": "application/json",
},
});
return response.json();
}
addEventListener("fetch", async (event: FetchEvent) => {
// Fetch data.
const packages = await getData("/packages");
// Render React components to a string.
const str = ReactDOMServer.renderToString(<App packages={packages} />);
// Prepend the DOCTYPE for better compatibility.
const body = `<!DOCTYPE html>${str}`;
const response = new Response(body, {
headers: { "content-type": "text/html; charset=utf-8" },
});
event.respondWith(response);
});
/// <reference path="https://raw.githubusercontent.com/denoland/deployctl/main/types/deploy.fetchevent.d.ts" />
/// <reference path="https://raw.githubusercontent.com/denoland/deployctl/main/types/deploy.window.d.ts" />
import * as React from "https://esm.sh/react@17.0.2";
import * as ReactDOMServer from "https://esm.sh/react-dom@17.0.2/server";
import { createElement as h } from "https://esm.sh/react@17.0.2";
// @ts-ignore Because VSCode occasionally complains that Deno is not defined.
const STRAPI_API_URL = Deno.env.get("STRAPI_API_URL") ||
"http://localhost:1337";
function App({ packages }: Props) {
return (
<html lang="en">
...
</html>
);
}
addEventListener("fetch", async (event: FetchEvent) => {
// Fetch data.
const packages = await getData("/packages");
// Render React components to a string.
const str = ReactDOMServer.renderToString(<App packages={packages} />);
// Prepend the DOCTYPE for better compatibility.
const body = `<!DOCTYPE html>${str}`;
const response = new Response(body, {
headers: { "content-type": "text/html; charset=utf-8" },
});
event.respondWith(response);
});

Execute with Deno

Deploy to Deno Deploy

Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store