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 application with Deno.
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 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 but any editor will do.
Open your terminal and type:
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.
// @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 as suggested in the documentation for 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 manual.
I’ve also decided to use Oak, a middleware framework for Deno's http server 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:
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 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 and the code in server.tsx
will look like this:
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 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:
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.
Top comments (15)
FYI at the time this article was published, the current version of Deno is v1.1.1 and all the JSX troubles have been fixed 😉
Edit: I believe the author has now removed the out of date warning regarding v1.0.2 for anyone confused by my now seemingly random comment!
Thanks for the sanity check!
I've actually wrote the code and the blog post way before it was scheduled to be published and forgot to check the current release, I'll update the post accordingly 👍
Nice one!
Yeah Deno is moving incredibly fast atm, so easy to draft something and then find it's already out of date! Already the case for a v. suspiciously similar article I wrote about a month ago on the same topic (REF: dev.to/craigmorten/writing-a-react...) - it's an effort to keep everything current 😂
Yeah totally agree, happens every time you start trying out new tech.
I guess it's one of the fun parts of been a developer!
P.S. JSPM has also migrated it's CDN from dev.jspm.io to jspm.dev. 😄
REF: jspm.org/jspm-dev-release
Yeah I saw that and updated the article even tho, as they state in the release,
dev.jspm.io
will still be available for a further 12 months until June 2021.Thanks for information. I got this error, code error: error sending request for url (dev.jspm.io/react@16.13.1): error trying to connect: dns error: No such host is known. (os error 11001). Deno 1.1.1 - Windows 10 - VsCode 1.46.1
edit: "Cannot find module 'dev.jspm.io/react-dom@16.13.1/server' or its corresponding type declarations.ts(2307)"
I'm not actually able to replicate the issue. This is the Deno version I'm running:
Have you tried by any chance to manually do :
and then:
Using the
--reload
flag will force a reload of all the modules you have already cached.I never downloaded before react-dom. I suppose their servers broken temporarly. Thank you.
Hey! Nice article! Where service do you suggest to host this deno-react ssr application?
I think the easiest way would be to create a Docker container for the app and then you should be able to deploy to any service you want.
You can have a look at Deno Docker to check how to set it all up.
Where does all the dinosaur artwork for Deno come from? I'm pretty unfamiliar with Deno, but I feel like things with a good mascot (e.g. Docker, Golang) often end up being successful... 😅
The original icon design was Ryan Dhal himself. A lot of the most popular banners/images in use are by the artist hashrock (including the banner of this article).
You can check out artwork and the artists on deno.land/artwork (not everything is on there, but by clicking through to artists’ GitHub you can generally find more). Be sure to give them credit!
Thank you!
nice tutorial!! maybe you can try Aleph.js, a react framework in deno, like nextjs.