DEV Community

Cover image for Writing a React SSR app in Deno
Craig Morten
Craig Morten

Posted on • Updated on

Writing a React SSR app in Deno

As featured in Open JS World 2020 by Ryan Dahl.

Deno v1 has shipped and is causing a real buzz in the JavaScript community.

For those that haven't come across it yet, Deno is a new runtime for JavaScript and TypeScript outside of the web browser. It based on the V8 JavaScript Engine, written in Rust and was created by Ryan Dahl, the original founder of Node.js.

If you want to find out more about Deno and it's mission, check out the Deno 1.0 launch blogpost written by the creators.

Background over, let's begin with writing our React SSR application in Deno!

Installation

Deno can be installed using all the main package installers as well using the official installer scripts. Here are some of the main ways to install:

Shell (Mac, Linux):

curl -fsSL https://deno.land/x/install/install.sh | sh
Enter fullscreen mode Exit fullscreen mode

PowerShell (Windows):

iwr https://deno.land/x/install/install.ps1 -useb | iex
Enter fullscreen mode Exit fullscreen mode

Homebrew (Mac):

brew install deno
Enter fullscreen mode Exit fullscreen mode

Chocolatey (Windows):

choco install deno
Enter fullscreen mode Exit fullscreen mode

Head over to the Deno installation page for other installation methods and further details!

Getting started

Please note that Deno is rapidly developing, which can quickly make articles like this one become outdated. Always check to see if there are newer versions of the modules used to ensure the code works for the latest version of Deno.

Having installed Deno you can now run make use of the deno command! Use deno help to explore the commands on offer. We'll be using this command to run our React SSR app later on.

But first let's create a project!

In a new project directory let's create three files:

.
├── app.tsx
├── client.tsx
└── server.tsx
Enter fullscreen mode Exit fullscreen mode

app.tsx will contain our React component code, server.tsx will hold all of our server code and client.tsx will act as our entrypoint to the client-side bundle. Be careful to get the correct file extensions!

Writing our client-side bundle

In the client.tsx file, add the following code to set up our client-side entrypoint:

import React from "https://dev.jspm.io/react@16.13.1";
import ReactDOM from "https://dev.jspm.io/react-dom@16.13.1";
import App from "./app.tsx";

(ReactDOM as any).hydrate(
  <App />,
  //@ts-ignore
  document.getElementById("root"),
);
Enter fullscreen mode Exit fullscreen mode

First we import React and React DOM like we're used to in any React app, but instead of importing from "react", we're importing it from a url...!?

That's right, in Deno you can import modules from any URL and relative or absolute file path that exports a module. This means you can easily pull in any code from the web, e.g. gists, GitHub code and are no longer tied to versions that have been released - if there's something on a main branch that you can't wait to try, you can just import it!

Here we are importing React and React DOM from JSPM, but you could equally use any CDN that provides React as an ES Module. Check out the Deno website for CDN alternatives.

Following our imports of React libraries we import our App component (yet to be written!) and finally set up the code to render our application, using the React DOM hydrate method.

Now let's write our first React component in Deno!

Writing the React component

Our app.tsx:

// @deno-types="https://raw.githubusercontent.com/Soremwar/deno_types/4a50660/react/v16.13.1/react.d.ts"
import React from "https://dev.jspm.io/react@16.13.1";

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

  return (
    <div>
      <h1>Hello Deno Land!</h1>
      <button onClick={() => setCount(count + 1)}>Click the 🦕</button>
      <p>You clicked the 🦕 {count} times</p>
    </div>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

There's a lot going on here, so let's break it down -

First we import React like we're used to in any React app, but notice we also are using a @deno-types hint comment. This allows us to inform deno of where to find the TypeScript types for the imported module - neat huh?

You can choose to omit this type hint, but TypeScript will require you to provide the types yourself (imported or custom written). Alternatively, you have avoid using TypeScript altogether simply by changing the file extension to .jsx. Deno supports both TypeScript and JavaScript out-of-the-box!

Lastly we create a small React component called App which uses hooks to create a button click counter - simple! Overall, there isn't much difference to writing a React component in NodeJS.

Writing the server

For the server we will be using the Deno web framework Opine, which is a port of the ExpressJS web framework that is commonly used in NodeJS.

Here's the code we'll be using for server.tsx:

// @deno-types="https://raw.githubusercontent.com/Soremwar/deno_types/4a50660/react/v16.13.1/react.d.ts"
import React from "https://dev.jspm.io/react@16.13.1";
import ReactDOMServer from "https://dev.jspm.io/react-dom@16.13.1/server";
import { opine } from "https://deno.land/x/opine@0.25.0/mod.ts";
import App from "./app.tsx";

/**
 * Create our client bundle - you could split this out into
 * a preprocessing step.
 */
const [diagnostics, js] = await Deno.bundle(
  "./client.tsx",
  undefined,
  { lib: ["dom", "dom.iterable", "esnext"] },
);

if (diagnostics) {
  console.log(diagnostics);
}

/**
 * Create our Opine server.
 */
const app = opine();
const browserBundlePath = "/browser.js";

const html =
  `<html><head><script type="module" src="${browserBundlePath}"></script><style>* { font-family: Helvetica; }</style></head><body><div id="root">${
    (ReactDOMServer as any).renderToString(<App />)
  }</div></body></html>`;

app.use(browserBundlePath, (req, res, next) => {
  res.type("application/javascript").send(js);
});

app.use("/", (req, res, next) => {
  res.type("text/html").send(html);
});

app.listen({ port: 3000 });

console.log("React SSR App listening on port 3000");
Enter fullscreen mode Exit fullscreen mode

Here's what is going on:

  1. First we import our main dependencies of React, ReactDOMServer and the Opine web framework.
  2. We then import the React app we just created, being careful to include the .tsx extension - file extensions are required by Deno unlike in NodeJS.
  3. First we use the Deno.bundle() method to create our client-side JavaScript bundle from our application.
  4. Next we create an Opine app, much like you would do with ExpressJs, and define some routes: one to serve a simple HTML page containing our rendered app, and another /browser.js route to server our app's client-side bundle so we can hydrate the React application on the client.
  5. Finally we start the server using the listen() method on port 3000.

And that's it! We're now ready to run our React application 🎉.

Running our React SSR application

We can now run our React SSR application using the following deno command:

deno run --allow-net --allow-read --unstable ./server.tsx
Enter fullscreen mode Exit fullscreen mode

Note the use of the various flags! A major difference between Deno and NodeJS is that Deno was build with security in mind. 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 will allow it.

To find out more, check out the Deno permissions section of the Deno Manual.

For our example application, we require --allow-net so that our server is allowed to access the network, --allow-read is required by the Opine framework (so it can read templates if make use of it's "views" feature), and we also need the --unstable flag to make use of the Deno.bundle() API, which is still in preview.

Head over to http://localhost:3000/ and voila! You should now see your React SSR application running in your browser. 😄

Next steps

This is just a basic server and app setup, but by now you should hopefully see that there isn't too much to do to convert your existing applications over to Deno.

That's all gang! Would love to hear your thoughts and how you're getting on with Deno - drop your comments below!


Update 29-06-2020: Deno is progressing quickly and all the aforementioned bugs with JSX are resolved, so I have removed references to them in this article!

Update 20-07-2020: If you followed this tutorial prior to the release of Deno 1.2.0 you will find that after upgrading there are several url related errors. Deno 1.2.0 brought about a breaking change for the std library so any module using std version before 0.61.0 may well error! Generally try seeing if modules you are using can be upgraded to a later version, and if that doesn’t fix it then try opening an issue on the repo you are having issues with!

Top comments (29)

Collapse
 
cindroberta profile image
cindRoberta

[notecomm@localhost react]$ deno run --allow-net ./server.tsx

error: TS2345 [ERROR]: Argument of type 'string | URL' is not assignable to parameter of type 'string'.
Type 'URL' is not assignable to type 'string'.
return new URL(url).pathname
~~~
at deno.land/std@0.59.0/path/win32.ts...

TS2345 [ERROR]: Argument of type 'string | URL' is not assignable to parameter of type 'string'.
Type 'URL' is not assignable to type 'string'.
return new URL(url).pathname;
~~~
at deno.land/std@0.59.0/path/posix.ts...

TS2345 [ERROR]: Argument of type 'string | URL' is not assignable to parameter of type 'string'.
Type 'URL' is not assignable to type 'string'.
return new URL(url).pathname
~~~
at deno.land/std@0.58.0/path/win32.ts...

TS2345 [ERROR]: Argument of type 'string | URL' is not assignable to parameter of type 'string'.
Type 'URL' is not assignable to type 'string'.
return new URL(url).pathname;
~~~
at deno.land/std@0.58.0/path/posix.ts...

TS2345 [ERROR]: Argument of type 'ParsedURL' is not assignable to parameter of type 'string'.
const loc = encodeUrl(new URL(originalUrl).toString());
~~~~~~~~~~~
at deno.land/x/opine@0.14.0/src/middl...

Found 5 errors.

github.com/apiel/adka/issues/1

Collapse
 
cindroberta profile image
cindRoberta

on deps.ts change
export { opine } from "deno.land/x/opine@0.19.1/mod.ts"; to

export { opine } from "deno.land/x/opine@0.19.1/mod.ts"; or
export { opine } from "deno.land/x/opine@main/mod.ts";

Collapse
 
craigmorten profile image
Craig Morten

Ah yes! Thanks for pointing this out.

Unfortunately the release of Deno 1.2.0 brought about some breaking changes meaning it no longer works with several std libraries pre 0.61.0

I will try to update this blog post to list the latest versions to use - unfortunately with the pace Deno is moving blog post become outdated very quickly 😂

Collapse
 
christoslitras profile image
Christos Litras • Edited

I get 27 TS1205 errors using latest Deno:

deno 1.5.4 (bc79d55, release, x86_64-pc-windows-msvc)
v8 8.8.278.2
typescript 4.0.5
Enter fullscreen mode Exit fullscreen mode

Errors:

Check file:///J:/Projects/deno-react-ssr/server.tsx
error: TS1205 [ERROR]: Re-exporting a type when the '--isolatedModules' flag is provided requires using 'export type'.
export { AsyncReturnType } from "./AsyncReturnType.ts";
         ~~~~~~~~~~~~~~~
    at https://deno.land/x/evt@v1.8.7/tools/typeSafety/index.ts:2:10

....

TS1205 [ERROR]: Re-exporting a type when the '--isolatedModules' flag is provided requires using 'export type'.
  Cookie,
Enter fullscreen mode Exit fullscreen mode
at https://deno.land/x/opine@0.21.2/deps.ts:16:3
Enter fullscreen mode Exit fullscreen mode

Found 27 errors.


Enter fullscreen mode Exit fullscreen mode
Collapse
 
craigmorten profile image
Craig Morten

Hey Christos 👋

This article was written back in May when Deno was on a much earlier version than 1.5.4! May I recommend you have a look at all of the Deno modules you import in the deps.ts to ensure you are using the latest version which (hopefully!) works for the version of Deno you are running.

For example, Opine's current latest version is 0.25.0, so updating to that may solve your issues 😄 (REF: github.com/asos-craigmorten/opine)

Let us know if upgrading modules works out for you!

Collapse
 
christoslitras profile image
Christos Litras

I've had wrong react-dom 16.3.1 instead od 16.13.1 and now that I've updated it and also opine to latest 0.25.0 I get hooks error:

error: Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.
...
at App (app.tsx:15:44)
...
Enter fullscreen mode Exit fullscreen mode

even after doing Deno reload using:

deno run --reload --allow-net --allow-read .\server.tsx
Enter fullscreen mode Exit fullscreen mode
Collapse
 
christoslitras profile image
Christos Litras

I understand that this article and example source code was written for an older Dyno version, but I believe it’s kind of useless if it’s not updated for the latest version and that’s not even a major version, it’s a minor version with some obvious breaking changes.

Having new to Deno developers installing an old version is not convenient.

Anyway, thanks for your time.

Thread Thread
 
craigmorten profile image
Craig Morten

Hi Christos, I'm sorry you felt my article was useless 😕

Over the past year, with every minor version of Deno there has been almost always a breaking change (even with just minor version upgrades), which has made it difficult to keep every demo and article up to date, and working with the latest versions, as this can take a lot of time.

I haven't and wouldn't ever recommend people use an older version 🙂 but I hope people come to expect that an old article might not work with a newer version after so many breaking changes 😅

If you find issues with any articles, what can be really useful for me (and all writers!) is if you are able to overcome the issues and leave comments on how to fix them - then the writer can easily update the article when they have time, or if not, at least others can read the comment and see the solution!

For now anyway, I have updated the article to work for Deno 1.5.4 (the latest). Please let me know if you have any further issues! I will always try to respond (eventually)! 😄

Best of luck with your Deno journey!

Thread Thread
 
christoslitras profile image
Christos Litras

Thank you Craig for making some time to update this article.

I didn't mean to offend you by saying the article is "useless", it just does not provide much value for begginers and new to Deno developers.

I have my own blog (lytrax.io) that I have many outdated articles and also many outdated answers in Stack Overflow and every single article has value; it's value just declines in time because it gets outdated and sometimes not even working with new versions.

Now for the updated version, I still get React hooks error in server.tsx line 29 where (ReactDOMServer as any).renderToString tries to render the <App/> component:

error: Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
...
 at server.tsx:29:29
Enter fullscreen mode Exit fullscreen mode

I tried to run this using Windows 10 and also macOS Catalina and getting the exact same error on both systems. I have created a Github repo here github.com/clytras/deno-react-ssr to make it easy test the code on different environments.

What am I doing wrong?

Again thanks for replying and updating the article! 👍

Thread Thread
 
craigmorten profile image
Craig Morten • Edited

Curious...! I'll check out your repo and see what I've missed 😄 Had it working locally when did the rewrite so maybe something lost in translation somewhere! 😅

Update:

So I ran the following:

$ mkdir clytras
$ cd clytras
$ git clone git@github.com:clytras/deno-react-ssr.git

Cloning into 'deno-react-ssr'...
coremote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 9 (delta 1), reused 8 (delta 0), pack-reused 0
Receiving objects: 100% (9/9), done.
Resolving deltas: 100% (1/1), done.

$ cd ./deno-react-ssr
$ deno upgrade

Looking up latest version
Local deno version 1.5.4 is the most recent release

$ deno --version

deno 1.5.4 (bc79d55, release, x86_64-apple-darwin)
v8 8.8.278.2
typescript 4.0.5

$ deno run --allow-net --allow-read --unstable ./server.tsx

Check ~/git/clytras/deno-react-ssr/server.tsx
React SSR App listening on port 3000
Enter fullscreen mode Exit fullscreen mode

Opened up http://localhost:3000 on both Firefox and Chrome (for MacOS) and could see the app running as intended without any console errors!

Firefox:

Deno React SSR application running in Firefox with developer console open and no errors displayed

Chrome:

Deno React SSR application running in Chrome with developer console open and no errors displayed

When you have run your updated application, have you checked to see if the old version of the application has been stopped! I commonly use port 3000 for apps and find all the time that I'm wondering why one app isn't right when I have a forked version running in a different window! (E.g. lsof -ti tcp:3000)

Otherwise, rather unhelpfully / frustratingly(!) your setup works on my machine! (MacOS Mojave 10.14.6)

If you don't make any progress debugging, can you try out the React example on the Opine repo (REF: github.com/asos-craigmorten/opine/...) - if that doesn't work we can get an issue raised and start debugging properly!

Thread Thread
 
christoslitras profile image
Christos Litras

Very strange!
It's not the port, I just tried a different free port (17123) and getting the same error and I don't get anything when I run lsof -ti tcp:3000 on macOS Catalina (10.15.7).

Here is the screenshot of the console and the error:

Deno React SSR hooks error

And Windows terminal screenshot:

Deno React SSR hooks error Windows

How can I debug this?

I also cloned opine and tried to run the example using deno run --allow-net --allow-read --unstable ./examples/react/server.tsx and I got an other error about invalid imports that are using the v infront of versions:

Warning std versions prefixed with 'v' were deprecated recently. Please change your import to https://deno.land/std@0.61.0/encoding/utf8.ts (at https://deno.land/std@v0.61.0/encoding/utf8.ts)
error: Import 'https://deno.land/std@v0.61.0/encoding/utf8.ts' failed: 404 Not Found
    at https://deno.land/x/dejs@0.8.0/vendor/https/deno.land/std/encoding/utf8.ts:1:0
Enter fullscreen mode Exit fullscreen mode

And this again for both Windows and macOS!
Does opine server running on your machine with Deno 1.5.4 without any modifications?

Collapse
 
horst profile image
Horst Schwarz

Hi Craig, thanks for the nice tutorial!

Launching the example with a today updated deno installation (deno 1.0.2, v8 8.4.300, typescript 3.9.2) fails with following errors:

deno run --allow-net --allow-read --reload "https://raw.githubusercontent.com/asos-craigmorten/deno-react-base-server/master/example/entrypoint.tsx" >> error.txt
Download https://raw.githubusercontent.com/asos-craigmorten/deno-react-base-server/master/example/entrypoint.tsx
Compile https://raw.githubusercontent.com/asos-craigmorten/deno-react-base-server/master/example/entrypoint.tsx
Download https://raw.githubusercontent.com/asos-craigmorten/deno-react-base-server/master/mod.tsx
Compile https://raw.githubusercontent.com/asos-craigmorten/deno-react-base-server/master/mod.tsx
error: TS2307 [ERROR]: Cannot find module 'https://dev.jspm.io/react@16.13.1' or its corresponding type declarations.
import React from "https://dev.jspm.io/react@16.13.1";
                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at https://raw.githubusercontent.com/asos-craigmorten/deno-react-base-server/master/mod.tsx:1:19

TS2307 [ERROR]: Cannot find module 'https://dev.jspm.io/react-dom@16.13.1/server' or its corresponding type declarations.
import ReactDOMServer from "https://dev.jspm.io/react-dom@16.13.1/server";
                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at https://raw.githubusercontent.com/asos-craigmorten/deno-react-base-server/master/mod.tsx:2:28

TS7006 [ERROR]: Parameter '_req' implicitly has an 'any' type.
  app.use(browserBundlePath, (_req, res, _next) => {
                              ~~~~
    at https://raw.githubusercontent.com/asos-craigmorten/deno-react-base-server/master/mod.tsx:27:31

TS7006 [ERROR]: Parameter 'res' implicitly has an 'any' type.
  app.use(browserBundlePath, (_req, res, _next) => {
                                    ~~~
    at https://raw.githubusercontent.com/asos-craigmorten/deno-react-base-server/master/mod.tsx:27:37

TS7006 [ERROR]: Parameter '_next' implicitly has an 'any' type.
  app.use(browserBundlePath, (_req, res, _next) => {
                                         ~~~~~
    at https://raw.githubusercontent.com/asos-craigmorten/deno-react-base-server/master/mod.tsx:27:42

TS7006 [ERROR]: Parameter '_req' implicitly has an 'any' type.
  app.use("/", (_req, res, _next) => {
                ~~~~
    at https://raw.githubusercontent.com/asos-craigmorten/deno-react-base-server/master/mod.tsx:31:17

TS7006 [ERROR]: Parameter 'res' implicitly has an 'any' type.
  app.use("/", (_req, res, _next) => {
                      ~~~
    at https://raw.githubusercontent.com/asos-craigmorten/deno-react-base-server/master/mod.tsx:31:23

TS7006 [ERROR]: Parameter '_next' implicitly has an 'any' type.
  app.use("/", (_req, res, _next) => {
                           ~~~~~
    at https://raw.githubusercontent.com/asos-craigmorten/deno-react-base-server/master/mod.tsx:31:28

Found 8 errors.

I'm unfortunatelly not an expert in deno. Do you have already an idea to solve these problems?

Collapse
 
craigmorten profile image
Craig Morten • Edited

Really sorry about that! I may have missed something when copying snippets from IDE to blog post! Alternatively it may be the Deno version - I tested this with Deno 1.0.0, and I know the newer versions broke some dependencies with type errors (namely the EVT Deno library had issues with v1.0.1 at one point!) - I’ll take a look when I can next, make fixes, and get back to you as soon as possible :)

In the meantime, you can try and see if it’s a Deno version by downgrading Deno to v1.0.0 - there instructions on how to install specific versions here: github.com/denoland/deno_install#i... (I’ll test myself as well!)

Collapse
 
craigmorten profile image
Craig Morten • Edited

I can confirm this code, including:

deno run --allow-net --allow-read --reload "https://raw.githubusercontent.com/asos-craigmorten/deno-react-base-server/master/example/entrypoint.tsx"

Appears to be broken for versions >= 1.0.1, so there must have been an accidental breaking change in Deno?

For now, the code should work fine on v1.0.0 (just tested) and I'll look to get it updated to be compatible with these latest versions of Deno :)

Further to my last comment, the easiest way to change to a specific version of Deno is to use the upgrade command:

deno upgrade --version 1.0.0

Edit:

I have found the relevant issues, and they should hopefully be resolved by the Deno team in the next Deno release. In the meantime I have updated the article to provide guidance on how to set Deno version to 1.0.0, added links to the issues and will look to update the code samples to support v1.0.1 and v1.0.2.

Edit 2:

Code and article updated to support Deno v1.0.2 🦕 🥳. Unfortunately Deno v1.0.1 support won't be possible due to a bug in the Deno core.

Upgrade to Deno v1.0.2 using the upgrade command:

deno upgrade --version 1.0.2
Thread Thread
 
horst profile image
Horst Schwarz

Thanks a lot!

Thread Thread
 
jmn profile image
jmn

With deno 1.0.2 I do still get Cannot find module 'https://dev.jspm.io/react@16.13.1' or its corresponding type declarations

Thread Thread
 
craigmorten profile image
Craig Morten

Oh no! 😢I’ll take another look - the underlying problem (as far as I know) is a bug in Deno which will be released in 1.0.3, check out github.com/denoland/deno/issues/5772

For now I believe v1.0.0 should be fine, so can downgrade in the meantime to get started.

Another open issue is that the unstable Import Map feature of Deno doesn’t play nicely with React atm - see github.com/denoland/deno/issues/5815

Off the top of my head, one thing to try is to use the Deno Types hint:

// @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"

Will test again myself and get back to you!

Thread Thread
 
craigmorten profile image
Craig Morten • Edited

Ok, I am able to reproduce! Here are the steps that I took:

a. Ran the Deno upgrade command to change to v1.0.2:

deno upgrade --version 1.0.2

b. Checked that I was definitely testing on Deno v1.0.2

deno --version

deno 1.0.2
v8 8.4.300
typescript 3.9.2

c. Cleared my Deno cache in case I had managed to get the module previously and cache it:

deno info

And then deleting everything inside the directories listed for Remote modules cache and TypeScript compiler cache.

d. Created an app.tsx and server.tsx as per this article.

e. Ran the following command:

deno run --allow-net ./server.tsx

And voila, errors:

Compile ~/server.tsx
error: TS2307 [ERROR]: Cannot find module 'https://dev.jspm.io/react@16.13.1' or its corresponding type declarations.
import React from "https://dev.jspm.io/react@16.13.1";
                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at ~/server.tsx:1:19

TS2307 [ERROR]: Cannot find module 'https://dev.jspm.io/react-dom@16.13.1/server' or its corresponding type declarations.
import ReactDOMServer from "https://dev.jspm.io/react-dom@16.13.1/server";
                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at ~/server.tsx:2:28

Found 2 errors.

In this instance, adding the @deno-types directive (though a good thing to do to get the React types) does nothing to solve the issue because Deno has the current bug that it's typescript compiler ignores all .tsx and .jsx files (see github.com/denoland/deno/pull/5785) and they are exactly the extensions we've been using!

Unfortunately neither Deno nor Typescript have a particularly great solution to this problem 😞 but there are 2 workarounds that you can use for now until v1.0.3 is released (if can find more then please comment!):

a. Use // @ts-ignore comments above each top-level React import statement. For example

// @ts-ignore
import React from "https://dev.jspm.io/react@16.13.1";
// @ts-ignore
import ReactDOMServer from "https://dev.jspm.io/react-dom@16.13.1/server";

Though I prefer option 2 (which I will use to update this blog post):

b. Create a dep.ts (note the .ts extension will mean that Deno v1.0.2 will compile it despite the bug). We will then import all of our dependencies into this file, and re-export them for use in our app.tsx and server.tsx:

// 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.3.0/mod.ts";
export {
  Request,
  Response,
  NextFunction,
} from "https://deno.land/x/opine@0.3.0/src/types.ts";
// app.tsx
import { React } from "./dep.ts";

// Rest of the app code stays the same
// server.tsx
import {
  opine,
  React,
  ReactDOMServer,
  Request,
  Response,
  NextFunction,
} from "./dep.ts";

// Rest of the server code stays the same

You can then run deno run --allow-net --reload ./server.tsx and it should start the application 🥳 🎉

Let me know how you get on!

Edit: Blog post all updated! Thanks so much for providing the feedback - articles become so much more useful to everyone through comments and pointing out bugs! I've certainly learned things 😄

Collapse
 
anborg profile image
anborg • Edited

Great tutorial, able to get it running within few minutes!

I'm new to JS world, have not used nodejs, and directly learning deno. I wanted to convert this little react js, and in the process learn deno & react :

github.com/anborg/react-pwa

Can someone help me to convert the above node to deno - to make it well organized source/subcomponent staticpage/images etc.

I am looking for an example deno-react project that is structurally similar e.g src/index.js, /public_html/index.html, writing into div "root" instead of rewriting "body", react subcomponents in separate subfolders etc.

Collapse
 
craigmorten profile image
Craig Morten

Hey @anborg ! 👋

Wow, I'd say a PWA React app is a reasonably complex thing! 😲

I have an example of how you can write an MVC like setup in another post including static CSS and EJS templates.

I've not seen any particularly complex examples of React + Deno - if you google there are a few examples / videos that use a mixture of Node and Deno for the desired effect. If you find any awesome examples then please do share!

I would be happy to create a more complex example React application which has some sub-components, uses static CSS etc. and tutorial to go with it - would that be useful?

Collapse
 
craigmorten profile image
Craig Morten

I’ve put together a quick example of using SSR React with suspense, static CSS and sub-components here —> github.com/asos-craigmorten/opine/...

I plan to write up a new post about it over the next couple of days.

Thread Thread
 
anborg profile image
anborg • Edited

@craig Thanks for the SSR. Would be great to have a CSR (client side rendering) React & Deno example, with a structured project so it can be used as a template to write maintainable production quality code.

Thread Thread
 
craigmorten profile image
Craig Morten • Edited

Hey sure, thanks for the suggestion. CSR would be very similar to the example, but instead of performing a server-side renderToString (here github.com/asos-craigmorten/opine/...) you would just not bother! Instead effectively setting the content of the React root (here github.com/asos-craigmorten/opine/...) as empty.

Finally instead of hydrating the content in your client-side script (here github.com/asos-craigmorten/opine/...), you would just use the reactDOM.render() method to create your app completely on the client. 🎉

For this particular example you could then likely refactor a couple of things such as the isServer prop that is used in this example to manage the fact that React Suspense is yet supported server-side.

Collapse
 
anborg profile image
anborg

Yes a simple react + deno + calling some weservice (e.g elasticsearch), subcompoenents, css etc. would serve as a production quality template!

Collapse
 
xtealer profile image
XTEALER

Great stuff, very promising!

Collapse
 
ahuigo profile image
ahuigo

Is there any way to import css/less module?

Collapse
 
craigmorten profile image
Craig Morten

Unfortunately CSS Modules etc. aren't there yet with Deno as they generally require a bundler to make them work.

You are able to use static CSS easily, see here for an example of how you can server static CSS.

I've also got a post on how to write a more complex app using static CSS here.

Fingers crossed tooling for things like CSS modules will be developed by the community shortly!

Collapse
 
ije profile image
X.

you can try Aleph.js, a react framework in deno, with built-in css/less support, or import sass with plugin.

Collapse
 
filippo profile image
Filippo Rossi

Hi Craig, nice tutorial!

I would suggest using denon, like nodemon but for deno, to ease the development process of this kind of apps.