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.
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)
After installation, you can run aleph -h
to check whether it got installed correctly.
Because of the portability of Deno, you can replace
aleph
withdeno 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
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
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.
- Importing Deno-Native Libraries: These libraries were either built for Deno, or ported over from npm to support Deno usage.
- 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>;
}
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>;
}
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>
);
}
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);
...
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"
/>
...
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>
...
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.
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 (
...
And we add this to the onSubmit
function in our form.
onSubmit={(e) => {
e.preventDefault();
submitData(siteURL);
}}
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";
...
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);
});
};
...
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
- https://dev.to/adriantwarog/react-deno-server-side-rendering-with-deno-ssr-4438
- https://alephjs.org/
- https://www.freecodecamp.org/news/build-a-url-shortener-in-deno/
- https://alephjs.org/docs/basic-features/routing
- https://alephjs.org/docs/basic-features/built-in-css-support
- http://deno.land/x
- https://esm.sh
- https://codesandbox.io/s/awesome-firefly-5dofg
- https://rapidapi.com/jakash1997/api/website-data-gathering-and-update-tracking
Top comments (7)
Doesn't work for me at all. Import statements for
react
andframework/react
give linting errorrelative import path "react" not prefixed with / or ./ or ../
.JSX returned from e.g.,
Home
gives linting errorJSX 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:What am I missing here? Is Aleph broken or do I need to install something else, change versions, do something differently? I am running:
Also, I don't know why Aleph is outputting this to
pages/index.tsx
:Found the problem. The
import_map.json
file was empty.Should be:
In case anyone else runs into this.
Thanks! I had the same problem.
Now I have another one when running
aleph build
:No idea why.
Ok, fixed by changing the import paths.
First, I tried changing the import map's first entry to this but didn't work:
Then I changed the import path themselves and it worked!
Got this working with:
Not sure what was broken before. Wrong version?
Hi @akash ,
What is the particular use case that deno solves which is not yet solved by regular ReactJS
Hey Anil, Deno isn't an alternative to React, it's an alternative to Node