Server Side Rendering React App with Deno
Tags: react, deno, typescript, javascript
Published: 2020-06-24 12:42:08

## Intro

Two of my favourites things are React and dinosaurs.
In this article I will show how I’ve put them together to develop a server side rendering [React](https://reactjs.org/) application with [Deno](https://deno.land/).

## Project Setup

I will assume that we are all familiar with React and Deno. Knowing that Deno is pretty new, if you don’t know how to install it and how it works, I would highly suggest you to have a read at this great [introduction](https://dev.p.ota.to/post/an-introduction-to-deno-4u3suut77w6/) before diving into this article.

Now let’s start creating the project structure and the files needed for this tutorial, I’m using [Visual Studio Code](https://code.visualstudio.com/) but any editor will do.
Open your terminal and type:
```zsh
mkdir deno-react-ssr && cd $_
code .
```
This will create a new folder called `deno-react-ssr` and will open it with vscode.
In this folder we will need to create three files, `app.tsx` that will contain the code of the React component, `server.tsx` for the server code and `deps.ts` will contain all our dependencies. Think of it as our version of a `package.json`.
You will end up with a structure like this:
```
.
├── app.tsx
├── deps.ts
└── server.tsx
```  

## Setting up the dependencies

In `deps.ts` we will have to export all the dependencies needed for this application to run.
Copy the following code and add it to your file.
```javascript
// @deno-types="https://deno.land/x/types/react/v16.13.1/react.d.ts"
import React from 'https://jspm.dev/react@16.13.1';
// @deno-types="https://deno.land/x/types/react-dom/v16.13.1/server.d.ts"
import ReactDOMServer from 'https://jspm.dev/react-dom@16.13.1/server';
export { React, ReactDOMServer }
export { Application, Context, Router } from 'https://deno.land/x/oak@v4.0.0/mod.ts';


```
As you can see, in Deno you import the modules directly from a url. 
I’ve decided to import React and ReactDOMServer from [jspm](https://jspm.org/) as suggested in the [documentation for third party modules](https://deno.land/#third-party-modules) but you can use any other CDN that provides the same modules.

One unusual thing that may stand out to you could be this:  
`// @deno-types="https://deno.land/x/types/react/v16.13.1/react.d.ts"`  
Since we are using typescript, this line of code will inform Deno of the location of the types it needs to import and will affect the `import` statement that follows. A more exhaustive explanation can be found in the [Deno Type Hint](https://deno.land/manual/getting_started/typescript#compiler-hint) manual.

I’ve also decided to use [Oak](https://github.com/oakserver/oak), a middleware framework for [Deno's http server](https://doc.deno.land/https/deno.land/std/http/mod.ts) that also provides a router, so I’m importing all the modules we will use in the server in addition to the `Context` type that typescript requires.

## Create your React component

This is how our `app.tsx` component will look:

```javascript
import { React } from "./deps.ts";

const App = () => {
  const [count, setCount] = React.useState(0);

  const garden = {
    backgroundColor: 'green',
    height: 'auto',
    fontSize: '30px',
    maxWidth: '400px',
    padding: '20px 5px',
    width: '100%'
  };

  return (
    <div className="pure-g pure-u">
      <h2>My DenoReact App</h2>
      <button className="pure-button" onClick={() => setCount(count + 1)}>Add a 🦕 in your garden!</button>
      <p style={garden}>
      { Array(count).fill(<span>🦕</span>) }
      </p>
    </div>
  );
};

export default App;

```

As with any standard React component, we start by importing React from our `deps.ts` file.

Then we are going to declare our App component that uses [hooks](https://reactjs.org/docs/hooks-intro.html) to implement a simple button counter that allows you to add as many dinosaurs as you want in your personal garden!

## Setting up the Server
For the server I’m using [Oak](https://github.com/oakserver/oak) and the code in `server.tsx` will look like this:
```javascript
import {
  Application,
  Context,
  React,
  ReactDOMServer,
  Router,
} from './deps.ts';

import App from "./app.tsx";

const PORT = 8008;

const app = new Application();
const jsBundle = "/main.js";

const js =
`import React from "https://jspm.dev/react@16.13.1";
 import ReactDOM from "https://jspm.dev/react-dom@16.13.1";
 const App = ${App};
 ReactDOM.hydrate(React.createElement(App), document.getElementById('app'));`;  
  

const html =
  `<html>
    <head>
      <link rel="stylesheet" href="https://unpkg.com/purecss@2.0.3/build/pure-min.css">
      <script type="module" src="${jsBundle}"></script>
    </head>
    <body>
      <main id="app">${ReactDOMServer.renderToString(<App />)}</main>  
    </body>
  </html>`;

const router = new Router();
router
  .get('/', (context: Context) => {
    context.response.type = 'text/html';
    context.response.body = html;
  })
  .get(jsBundle, (context: Context) => {
    context.response.type = 'application/javascript';
    context.response.body = js;
  });

app.use(router.routes());
app.use(router.allowedMethods());

console.log(`Listening on port ${PORT}...`);

await app.listen({ port: PORT });
```

As always we need to import all the dependencies we will use in our server. 
We will also import our App we created earlier, as you can see the extension `.tsx` is required in Deno so don’t forget it!  
Next step is to create our Oak server application and we’ll also need to define some routes:  
* `'/'` will serve our HTML page that contains the rendered app.  
* `'/main.js'`  will serve our application code that is needed to [hydrate](https://reactjs.org/docs/react-dom.html#hydrate) the client side React application.

Finally we tell our application to use the route we just created and start listening on port `8008`. You can notice I’m also using `router.allowedMethods()`, it’s a middleware that lets the client know when a route is not allowed.

## Run the application

Running the SSR React application we just created is extremely simple, you just need to use the following command:
```zsh
deno run --allow-net ./server.tsx
```

Deno is built secure by default, that means that a Deno application will not be able to access your network, to overcome this we'll just need to use Deno's `--allow-net` flag.  
Now the only thing missing is to open `http://localhost:8008/` and enjoy your new App!

## Conclusion

I hope you enjoyed the brief tutorial illustrated in this article and I’m looking forward to seeing what will happen next and how more complex applications can be built with this stack.

If you are still unclear about anything we’ve done or want a full reference of the code, here’s the [GitHub repository](https://github.com/fleonard/deno-react-ssr).