DEV Community

Cover image for Writing a React SSR app in Deno

Writing a React SSR app in Deno

Craig Morten on May 21, 2020

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 have...
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.