In this post, we will create a simple React app that is server-side rendered using Deno.
Over the years, browsers have evolved and are now more powerful. We started to create entire websites and web apps with client-side JavaScript. This is called “Single Page Application.” This leads us to create more interactive real-time updated web applications.
Now the alternative to rendering on the client-side is the use of server-side rendering. In this post, we will take that alternative route to create a simple React app that is server-side rendered using Deno. Before we dive in, let’s talk a bit about server-side rendering.
What is server-side rendering? It is a technique for rendering a normally client-side only Single-Page App (SPA) on the server and then sending a fully rendered page to the browser. Alternatively, in client-side rendering the initial HTML rendered by the server is a placeholder and the entire user interface and data are rendered in the browser once all your scripts load.
Create a project directory:
mkdir deno-ssr
cd deno-ssr
We need to import our dependencies from their URL, and also export them to be used in their appropriate files. Create the dep.ts
file inside the project folder and paste the following code.
touch dep.ts
export { default as React } from "https://dev.jspm.io/react@16.13.1";
export { default as ReactDOMServer } from "https://dev.jspm.io/react-dom@16.13.1/server";
export { opine } from "[https://deno.land/x/opine@0.21.2/mod.ts](https://deno.land/x/opine@0.21.2/mod.ts)";
Note: I used jspm to import React and ReactDOMServer, but you can use any other CDN that provides the same modules.
Now, create the following files inside the project folder.
touch server.tsx
touch app.tsx
This is how our app.tsx
component will look:
import { React } from "./dep.ts";
// import './style.css'
declare global {
namespace JSX {
interface IntrinsicElements {
button: any;
div: any;
h1: any;
p: any;
}
}
}
const App = () => {
return (
<div className="app">
<h1>Hello! i was ready before i got here</h1>
</div>
);
}
export default App;
First, we import React. Then we declare some intrinsic elements for TypeScript to use when compiling our App. Lastly, we created a React component called App that uses a hook to change the text.
Note: I’ve cast React as any in this example, but you can equally use fully typed React by importing the types from the DefinitelyTyped GitHub repo or by using the Deno Types hint above any import lines for React. For example:
// @deno-types="https://deno.land/x/types/react/v16.13.1/react.d.ts"
import React from "https://dev.jspm.io/react@16.13.1"
Here’s the code we’ll be using for server.tsx
:
import {
opine,
React,
ReactDOMServer,
} from "./dep.ts";
import App from "./app.tsx";
const app = opine();
const browserBundlePath = "/browser.js";
const js =
`import React from "https://dev.jspm.io/react@16.13.1";\nimport ReactDOM from "https://dev.jspm.io/react-dom@16.13.1";\nconst App = ${App};\nReactDOM.hydrate(React.createElement(App), document.body);`;
const html =
`<html><head><script type="module" src="${browserBundlePath}"></script><style>* { font-family: Helvetica; }</style></head><body>${
(ReactDOMServer as any).renderToString(<App />)
}</body></html>`;
// server our app's code so we can hydrate the React application on the client
app.use(browserBundlePath, (req, res, next) => {
res.type("application/javascript").send(js);
});
// serve a simple HTML page containing our rendered app
app.use("/", (req, res, next) => {
res.type("text/html").send(html);
});
app.listen({ port: 3000 });
console.log("App listening on port 3000");
Here’s what is going on: First, we import our main dependencies; we then import the React app we just created.
Note: Unlike Node.js, in Deno file extensions are required, so be careful to include the
.tsx
extension.
Next, we created an Opine app, much like you would do with Express.js, and define some routes: one to serve a simple HTML page containing our rendered app, and another /browser.js route to serve our app’s code so we can hydrate the React application on the client. Finally, we start the server using the listen() method on port 3000.
Note: Hydration is the entire process of putting functionality back into the HTML that was already rendered in server-side React. So basically it is the process of re-rendering over the once-rendered HTML.
We can now run our React SSR application using the following deno
command:
deno run --allow-net --allow-read ./server.tsx
Make sure to add the appropriate flags as specified. Any action that needs to access the web, read or write to files, or even consume environment variables needs to have the permission granted before Deno allows it.
If the following are important for your project, then you need to consider using SSR.
I hope you enjoyed the brief tutorial illustrated in the post. Keep in mind this is just a basic server and app setup intended to give you a foundational understanding. From here, you can go on to create or port more complex applications.
Chinedu is a tech enthusiast focused on full-stack JavaScript and Infrastructure engineering.