DEV Community

Cover image for Building React Applications using Deno: The Definite Guide
Akash Joshi
Akash Joshi

Posted on

Building React Applications using Deno: The Definite Guide

For those just started with Deno, and those who work in the frontend, you might be having the thought, "Can I build something as complex as a NextJS or Create-React-App (CRA) application using Deno?"

I was thinking the same as I wanted to try Deno because of its better shareability resulting from the ability of running an application directly from a URL (The Deno compiler supports running JS/TS files from a URL and it also supports imports from a URL, resulting in extreme portability.)

I looked if any existing solutions, articles were there online, but only found this article, which built an SSR'ed React application using some complex techniques, nothing simple like getting started with NextJS or CRA.

So, through my searches online, I ended up at AlephJS, which has one of the coolest landing page animations ever.

https://www.freecodecamp.org/news/content/images/2021/03/Kapture-2021-03-11-at-11.33.15-4.gif

Aleph is a Zero-Config, Typescript driven React framework, just like NextJS, the only drawback being that Aleph is still very much in Alpha.

So to get a true Next-like React experience inside of Deno, let's get started with AlephJS. It has much of the same conventions, such as:

  • A /pages directory for creating URL routes
  • Direct .js, .jsx, .ts, .tsx support in pages
  • A /public directory for serving static assets like video, audio, or image files
  • A /pages/api folder for serving Javascript or Typescript files as serverless APIS.

Getting Started

To be able to use AlephJS, you need Deno installed as a pre-requisite. You can see how to install and get started with Deno in my previous article here.

For getting started with Aleph, you need to first install the Aleph CLI by running

deno install -A -f -n aleph [https://deno.land/x/aleph@v0.3.0-alpha.1/cli.ts](https://deno.land/x/aleph@v0.3.0-alpha.1/cli.ts)
Enter fullscreen mode Exit fullscreen mode

After installation, you can run aleph -h to check whether it got installed correctly.

Because of the portability of Deno, you can replace aleph with deno run -A [https://deno.land/x/aleph@v0.3.0-alpha.1/cli.ts](https://deno.land/x/aleph@v0.3.0-alpha.1/cli.ts) start $app_URI for any command and it will be able to run the Aleph program without having the CLI locally installed.

To create a starter app, run:

aleph init hello
cd hello
Enter fullscreen mode Exit fullscreen mode

And start the app in development mode using aleph dev to start a server at port 8080.

To start the app in production mode, you have to first build the app and then run the built app. This is done through the commands:

aleph build # build your app
aleph start # runs built app
Enter fullscreen mode Exit fullscreen mode

After you initialise your Aleph app, you will find the root component being defined at pages/index.tsx. It's a normal React component. Experiment with it to see how Aleph works.

You can add more routes to your application by creating more .jsx or .tsx files inside the pages folder. Read more on routing here.

Importing Libraries

I've written about Deno previously on freeCodeCamp, where I demoed Deno basics, including URL imports. Since Aleph is a Deno framework, all imports happen in the "Deno way".

There are 2 kinds of libraries which you can import in a Deno application.

  1. Importing Deno-Native Libraries: These libraries were either built for Deno, or ported over from npm to support Deno usage.
  2. Importing from NPM: Any dev who has worked with JS recently knows about npm. If you don't, npm (which used to stand for node package manager) is the standard repository for all Javascript libraries. Luckily, Deno has limited support for npm libraries. Using tools like esm.sh or skypack.dev, users can import npm libraries into Deno.

1. Importing Deno-Native Libraries

The way to import Deno-Native libraries in your application is by importing their URL directly. You can find a list of Deno libraries here: deno.land/x

To test this out, let’s import this standard Deno date formatting library, and calling a date format function in a React page. Create a file date-import.tsx in the pages folder of your app. Inside the file, write the following code:

// react is a compulsoy import in Aleph
import React from "react";

// import the format function from its URL
import { format } from "https://deno.land/std@0.88.0/datetime/mod.ts";

// capitalize the function name so it's recognized as a React component
export default function DateImport() {
    // Here, directly calling the format function works as expected.
  return <section>Hello all! Today is: {format(new Date(), "dd-MM-yyyy")}</section>;
}
Enter fullscreen mode Exit fullscreen mode

To see the output of this file, go to localhost:8080/date-import, or its equivalent for your server. You should see the page displaying the day's date.

2. Importing from NPM

To import an npm library, you can also import directly from a URL, but in this case there's a slight change. Since we talked about esm.sh and skypack.dev, let's try to use them in action. In this case, let's try to use the dayjs library in our project.

Note: Not all npm libraries work correctly in Deno because they may be relying on Node-specific functions.

To import a library in esm.sh, you post-pend the library's package name to the URL. In this case to import dayjs, we would be importing https://esm.sh/dayjs. This also works for any CSS files you might want to import from a library.

Now, let's create a file in pages called dayjs-import.tsx. So, the code in our page will look like:

// react is a compulsoy import in Aleph
import React from "react";

// import the dayjs npm library using esm.sh
import dayjs from "https://esm.sh/dayjs";

// capitalize the function name so it's recognized as a React component
export default function DateImport() {
    // call the dayjs function directly to display today's date
  return <section>Hello all! Today is: {dayjs().format("DD-MM-YYYY")}</section>;
}
Enter fullscreen mode Exit fullscreen mode

To see the output of this file, go to localhost:8080/dayjs-import, or its equivalent for your server. You should see the page displaying the day's date.

There's one important thing before we go ahead though, how do you handle React imports like importing useState, useEffect, etc? Luckily, the devs of aleph have already written an example for us. Go into ./lib/useCounter.ts and you'll find the code for the custom hook being used for the counter in the home page.

Since I want to focus the article on Aleph and React itself, to check out all the different ways you can import a CSS file in Aleph, check out this page from the official documentation.

Building a Sample App

Now, let's get into the nitty gritty and try to build a React app in Aleph ourselves. We're going to be building "Is It Down?", a sample app I had made using an existing website checking API. This app will allow us to check whether a website is currently up or down.

Codesandbox link: https://codesandbox.io/s/awesome-firefly-5dofg

Building this application will show how to use the State hook, the Effect hook and making API calls inside of Aleph.

Create a new file called web-checker.tsx in your pages folder. Let's start by just adding the UI elements first. We'll display an h1 element with the title, an h2 element linking to the API and a form element to take user input. This is a non-interactive page just displaying the elements.

import React from "react";

export default function App() {
    return (
    <div style={{ fontFamily: "sans-serif", textAlign: "center" }}>
      <h1>Is it Down?</h1>
      <h2>
        Go{" "}
        <a
          href="https://rapidapi.com/jakash1997/api/website-data-gathering-and-update-tracking"
          target="_blank"
        >
          here
        </a>{" "}
        to get an API key
      </h2>

      <form
        onSubmit={(e) => {
          e.preventDefault();
        }}
      >
        <input
          type="text"
        />
        <button type="submit">Submit</button>
      </form>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Next, to capture the state of the input field, and to also capture the response of the API call we will have to make, let's introduce state.

// import useState from react
import React, { useState } from "react";

export default function App() {
  // define both state variables
  const [siteURL, setUrl] = useState("");
  const [response, setResponse] = useState(undefined);
...
Enter fullscreen mode Exit fullscreen mode

Now, we'll use this state inside our input element, so it can react to it.

...
<input
  value={siteURL}
  onChange={(e) => setUrl(e.target.value)}
  type="text"
/>
...
Enter fullscreen mode Exit fullscreen mode

We'll also add some code to display a response, when it's returned from the API response

...
    </form>

    <br />

    <code>{JSON.stringify(response, null, 2)}</code>
</div>
...
Enter fullscreen mode Exit fullscreen mode

Now, to get started with integrating the API, let's try to form the request correctly. In this case, the API is a simple GET call, so we only need to pass a param and an API key.

Firstly, go here, and generate an API key: https://rapidapi.com/jakash1997/api/website-data-gathering-and-update-tracking. Find the API key like shown in the screenshot and keep it somewhere safe.

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d0a52695-3374-40ca-91e0-5eee7834a4fe/Screenshot_2021-03-08_at_3.47.01_PM.png

Next, let's create a seperate function submitData which will generate the required request data. We will be using the axios library to make our GET call, so we will be forming its options object.

...
const [response, setResponse] = useState(undefined);

const submitData = (siteURL) => {
  setResponse("Loading...");
  const options = {
        // passing siteURL here through object shorthand
    params: { siteURL },

        // passing the required headers here
    headers: {
      "x-rapidapi-key": "YOUR_API_KEY",
      "x-rapidapi-host":
        "website-data-gathering-and-update-tracking.p.rapidapi.com",
    },
  };

    // print options here
    console.log("options", options);
};

return (
...
Enter fullscreen mode Exit fullscreen mode

And we add this to the onSubmit function in our form.

onSubmit={(e) => {
  e.preventDefault();
  submitData(siteURL);
}}
Enter fullscreen mode Exit fullscreen mode

Now, whenever you press the Submit button, you will see the options we generated in the console. If you see the options object in the console, you're doing good so far!

Next we just have a simple step of importing the axios library using [esm.sh](http://esm.sh) and using it to make an API call.

Import axios after the react import like:

import React, { useState } from "react";
import axios from "https://esm.sh/axios";

...
Enter fullscreen mode Exit fullscreen mode

And use it in the submitData function as:

...
    axios
    .get(
      "https://website-data-gathering-and-update-tracking.p.rapidapi.com/sitecheck",
      options
    )
    .then(function (response) {
      setResponse(response.data);
      console.log(response.data);
    })
    .catch(function (error) {
      console.error(error);
    });
};
...
Enter fullscreen mode Exit fullscreen mode

And that's it! Try submitting the form again and this time, you'll see the result both on screen, and also in the console.

So that has been Aleph, a really interesting tool which allows you to bring your existing React knowledge, and mix it with the forward-looking nature and security of deno.land.

If you liked this tutorial, you can follow me on Twitter @thewritingdev

Important Links

Top comments (7)

Collapse
 
chasm profile image
Charles F. Munat

Doesn't work for me at all. Import statements for react and framework/react give linting error relative import path "react" not prefixed with / or ./ or ../.

JSX returned from e.g.,Home gives linting error JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.deno-ts(7026). I presume this is because it isn't finding React.

Running the app returns 500 - Cannot resolve module "file:///**/hello/.aleph/development/react" from "file:///**/hello/.aleph/development/app.js#e5507c". at file:///**/hello/.aleph/development/app.js#e5507c:3:0

aleph build also fails:

Bundle /_aleph/shared.bundle.entry.js
error: Unable to output bundle during Graph::bundle().

Caused by:
    0: load_transformed failed
    1: failed to analyze module
    2: failed to resolve ./react from </_aleph/app.bundling.js>
    3: The graph is missing a dependency.
         Specifier: /_aleph/react from /_aleph/app.bundling.js
Enter fullscreen mode Exit fullscreen mode

What am I missing here? Is Aleph broken or do I need to install something else, change versions, do something differently? I am running:

deno 1.8.2 (release, x86_64-apple-darwin)
v8 9.0.257.3
typescript 4.2.2
Enter fullscreen mode Exit fullscreen mode

Also, I don't know why Aleph is outputting this to pages/index.tsx:

import Logo from '~/components/logo.tsx'
import useCounter from '~/lib/useCounter.ts'
Enter fullscreen mode Exit fullscreen mode
Collapse
 
chasm profile image
Charles F. Munat

Found the problem. The import_map.json file was empty.

{
    "imports": {}
}
Enter fullscreen mode Exit fullscreen mode

Should be:

{
  "imports": {
    "~/": "./",
    "aleph/": "https://deno.land/x/aleph@v0.3.0-alpha.24/",
    "framework": "https://deno.land/x/aleph@v0.3.0-alpha.24/framework/core/mod.ts",
    "framework/react": "https://deno.land/x/aleph@v0.3.0-alpha.24/framework/react/mod.ts",
    "react": "https://esm.sh/react@17.0.2",
    "react-dom": "https://esm.sh/react-dom@17.0.2"
  },
  "scopes": {}
}
Enter fullscreen mode Exit fullscreen mode

In case anyone else runs into this.

Collapse
 
laurogripa profile image
Lauro Gripa

Thanks! I had the same problem.

Now I have another one when running aleph build:

ERROR module '/pages/components/logo.tsx' not found
ERROR module '/pages/lib/useCounter.ts' not found
Enter fullscreen mode Exit fullscreen mode

No idea why.

Thread Thread
 
laurogripa profile image
Lauro Gripa • Edited

Ok, fixed by changing the import paths.

First, I tried changing the import map's first entry to this but didn't work:

    "~/": "../",
Enter fullscreen mode Exit fullscreen mode

Then I changed the import path themselves and it worked!

import Logo from '../components/logo.tsx'
import useCounter from '../lib/useCounter.ts'
Enter fullscreen mode Exit fullscreen mode
Collapse
 
chasm profile image
Charles F. Munat

Got this working with:

deno run -A https://deno.land/x/aleph/install.ts
aleph -h
aleph init hello
cd hello
aleph dev
Enter fullscreen mode Exit fullscreen mode

Not sure what was broken before. Wrong version?

Collapse
 
anilpank profile image
Anil

Hi @akash ,
What is the particular use case that deno solves which is not yet solved by regular ReactJS

Collapse
 
thewritingdev profile image
Akash Joshi

Hey Anil, Deno isn't an alternative to React, it's an alternative to Node