Building a frontend in the modern era is tough because of the plethora of choices you must make. Developers will often reach for a popular framework, such as React, and find themselves needing more tools to get the job done.
These tools could include a bundler, test runner, and linter. Not only that, but they need to consider SEO, styling assets, routing, data fetching, and the list goes on. Developers should evaluate all of these when creating a production-ready, performant React app.
Projects like create-react-app and Next.js have gained popularity for providing features that were tedious to put in place on their own. Deno is a new JavaScript runtime that is gaining support from the community. Deno aligns with established web standards by supporting ES modules, import maps, and the Fetch API.
Most of today’s React frameworks are only supported to render on the server using Node.js, but some new frameworks do support Deno. Deno offers many inbuilt tools for functionality that Node.js frameworks must provide on their own.
Deno supports ES modules and TypeScript, so these newer frameworks can avoid build steps, like transpilation. Deno also has a large standard library, developer tools for common tasks (like linting, formatting, and testing), and a package manager.
Some developers are wary of Deno because it does not support npm and is not compliant with all Node.js third-party packages. In my experience, there are many workarounds to these limitations.
Ruck and Aleph.js are Deno-native React web frameworks; both support features like server-side rendering, data-fetching, routing, and modifying the HTTP server response.
In this article, we’ll discuss the key similarities and differences between Ruck and Aleph.js that are important to evaluate when choosing which framework to use.
Jump ahead:
Ruck is a minimal framework for building React apps with Deno. It leans into Deno-specific features like ES modules and import maps, making it a great showcase for the new runtime.
However, Ruck doesn’t use a bundler, so it does not support writing React components in JSX and all configuration is defined in code. Using createElement
everywhere is not the best developer experience!
I could see another framework adopting Ruck under the hood to address a lot of these problems. Ruck is what you’re looking for if you want to have control over everything that is going on and don’t like the “magic” of most frameworks.
Here’s an example component written with Ruck:
import { createElement as h } from "react"; import useOnClickRouteLink from "ruck/useOnClickRouteLink.mjs"; import useRoute from "ruck/useRoute.mjs"; export const css = new Set([ "/components/ExampleComponent.css", ]); export default function ExampleComponent({ href, children }) { return createElement("a", { className: "NavLink__a", href, onClick: () => console.log('Hello World!') }, children ); }
Aleph.js is a full-stack web framework for building React apps with Deno. It is second only to Fresh, as the most popular Deno-native React framework. It leans into Deno for some of its features but also provides much more.
Aleph.js is inspired by Next.js; it even gives the same syntax for some features. Aleph.js supports server-side rendering as well as static-site generation, creating standalone APIs, file-base routing, and Hot Module Reloading. To support separate file types, such as JSX and CSS, Aleph.js uses esbuild instead of webpack.
Here’s an example component written in Aleph.js:
import React from 'react'; import Logo from '../components/logo.tsx export default function ExampleComponent() { return ( <div> <Logo /> <h1>Hello World!</h1> </div> ) }
There are many similarities between Ruck and Aleph.js, such as inbuilt support for import maps. Without the usage of npm or another package manager, Deno depends on HTTP imports. This means imports usually look like this:
import React from "<https://esm.sh/stable/[email protected]/es2021/react.js”>;
Deno recommends putting all module imports into a single deps.ts
file to be re-exported. The issue with this approach is that the imports are still incompatible with Node.js/webpack counterparts.
A better way (and browser-compliant way) to handle this is with import maps. Import maps are a recent browser feature that lets the browser know where a module’s dependencies are located.
Here’s an example of an import map:
{ "imports": { "react": "<https://esm.sh/stable/[email protected]/es2021/react.js>", } }
Here’s a component that uses the import map:
import React from "react"; export default function ExampleComponent() { return <div />; }
To use an import map in Aleph.js, we need to define a file named import_map.json
in the root directory. It’s also simple to use an import map in Ruck; we define the file and pass it into Deno at runtime, like so:
deno run \\ --allow-env \\ --allow-net \\ --allow-read \\ --import-map=importMap.json \\ scripts/ruck-serve.mjs
The issue with import maps is that browser support is still poor; neither Safari nor Firefox offers inbuilt support. The good news is that Ruck uses a shim to provide support for older browsers.
Another similarity between Ruck and Aleph.js is their focus on server-side rendering React components. SSR can provide enhanced performance, SEO, and other benefits compared to client-side rendering.
If a React component depends on fetched data, opting to do so on the server means a component can render before sending data to the client. This means no loading states to show to the user and generally better performance.
Ruck supports data fetching on the server at a component level, whereas other frameworks usually only support this at the page level. Aleph.js lets you achieve this by defining an SSR function inside a page component file. Aleph.js also supports a special hook, useDeno
, for use in a component.
Here’s an example showing the use of useDeno
to fetch data on the server side in Aleph.js:
import React from 'react' import { useDeno, useRouter } from 'aleph' export default function Post() { const { params } = useRouter() const post = useDeno(async () => { return await (await fetch(`https://.../post/${params.id}`)).json() }) return ( <h1>{post.title}</h1> ) }
When it comes to styling a React app with CSS, both Ruck and Aleph.js support component-level CSS imports. This allows for sending CSS when the browser requests it, such as when a component renders.
Ruck allows for this via an exported component variable named css
. You can achieve the same behavior in a variety of ways with Aleph.js, but the recommended approach is to use CSS modules.
Here’s an example showing the use of the css
function in Ruck:
import React from 'react' import Heading, { css as cssHeading } from "./Heading.mjs"; import Para, { CSS as CSS paragraph } from "./Para.mjs"; export const css = new Set([ ...cssHeading, ...cssParagraph, "/components/ExampleComponent.css", ]); export default function ExampleComponent() { ... }
Here’s an example demonstrating the use of a css
module in Aleph.js:
import React from 'react' import styles from './exampleComponent.module.css' export default function ExampleComponent() { return ( <> <h1 className={styles.title}>Hi :)</h1> </> ) }
One perk of being a server-side rendered application is having access to the HTTP request during the rendering lifecycle. This can be helpful if you need to access headers or change the response.
With Ruck, the HTTP response is available in a React context, TransferContext
. In Aleph.js we can use the SSR
function.
Here’s an example of modifying the HTTP response in Ruck:
import React from 'react'; import TransferContext from "ruck/TransferContext.mjs"; export default function PageError({ errorStatusCode, title, description }) { const ruckTransfer = useContext(TransferContext); if (ruckTransfer) ruckTransfer.responseInit.status = errorStatusCode; ... }
Here’s an example of modifying the HTTP response in Aleph.js:
import React from 'react'; import { useDeno } from 'aleph'; export default function ExampleComponent() { const isLoggedIn = useDeno(req => { return req.headers.get('Auth') === 'XXX' }, { revalidate: true }) return ( <p>isLoggedIn: {isLoggedIn}</p> ) }
There are notable differences between the Ruck and Aleph.js frameworks, particularly concerning popularity and developer experience. Because Ruck is new, it lacks the community backing associated with more established frameworks like Aleph.js.
Aleph.js has 4.7k GitHub stars compared to 94 for Ruck. Of course, GitHub star count is not always the best measure of a framework’s functionality but gives you a good sense of developer intent.
Ruck favors configuration over convention. It caters to developers who like a high level of control over exactly how their application functions. For example, with Ruck, you must define how your web application router functions entirely yourself while Aleph.js handles most of this for you. You can see the example router on the Ruck repository’s README for instance.
Aleph.js can be run with zero configuration and provides project templates to get developers started. You can opt-in to features based on config. With Ruck, you must spend time setting up the basics of the application yourself.
Static websites are desirable if your web application has all the data it needs at build time. This can simplify deployments as there is no need for a running Deno server. Place the built folder of HTML, CSS, and JavaScript to a deployment target like GitHub Pages or Cloudflare.
Aleph.js supports static-site generation, which is helpful for these situations, while Ruck does not. Like getStaticPaths
in Next.js, you can define a path’s key in the ssr
function inside a component file to specify the paths this route can handle:
import type { SSROptions } from 'aleph/types'; export const ssr: SSROptions = { paths: async () => { const posts = await (await fetch('https://.../api/posts')).json() return posts.map(({ id }) => `/post/${id}`) } }
Next, run aleph build
; it’s as simple as that!
As Deno continues to grow in popularity, both Ruck and Aleph.js should be considered viable options for building react apps in Deno. These Deno-based React web frameworks cater to two different sets of developers. As Ruck is a newcomer, it doesn’t have the same level of polish as Aleph.js, but it offers more control.
Aleph.js provides a great developer experience with zero config needed and lots of powerful features. These minimal frameworks offer many inbuilt modern browser features which can lead to a minimal and lean tech stack. This contrasts with a lot of the complexity in the frontend ecosystem seen today.
Deno’s large number of inbuilt features results in less work being done by third-party tools. React frameworks can focus on developing innovative and interesting new features while developers can be at ease knowing they made a great choice for their web application tech stack.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowCompare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.