React is awesome but have you tried fresh?

ยท

5 min read

Lemme start by asking you a simple question -- "Have you used react?" or have you used any JS framework or library for creating web apps like vue, svelte, lit, nextJS or anything like that. You know what's the one thing that's common in all of them? They are built on top of nodejs, have lots of boilerplate code and install tons of other libraries and use that evil node_modules folder.

What if I said, there's a framework which doesn't have any of these issues, has minimum boilerplate, no node_modules and is built on top of deno. I'm talking about fresh here. It's a web framework built on top of deno, and recently got out of beta and is getting decent amount of attention from the JS ecosystem. The creators of fresh call it "The next gen web framework", sounds cool right!

Some features that make fresh stand out --

  • Just-in-time rendering on the edge.
  • Island based client hydration for maximum interactivity.
  • Zero runtime overhead: no JS is shipped to the client by default.
  • No build step.
  • No configuration necessary.
  • TypeScript support out of the box.

In this blog, I'll walk you through the basics of the framework and we will be building the classic ToDo app as always, so let's start!

Prerequisite: you should have deno installed on your machine you can install it using this.

Let's start by scaffolding our project, to create a new fresh project and run it you need to run these command in your terminal. Make sure you use twind when you are prompted for it by fresh.

deno run -A -r https://fresh.deno.dev todo-app
cd todo-app
deno task start

This will start your app on localhost:8000, with a basic counter app.

ss1

Let's understand the basic concepts now, a fresh project has a total of 8 components, I'll be covering only the routes/ and islands/ folder in this blog, you can learn about all of them here.

  • routes/: This folder contains all of the routes in your project. The names of each file in this folder correspond to the path where that page will be accessed. Code inside of this folder is never directly shipped to the client
  • islands/: This folder contains all of the interactive islands in your project. The name of each file corresponds to the name of the island defined in that file. Code inside of this folder can be run from both client and server.

In simpler terms, to add interactivity and reactivity to your project, you need to use islands and to create pages/routes you need to use routes.

Let's start by creating a new todo route in the app where we will build our todo app. Add a file named todo.tsx inside routes folder with the below content. We will be using twind to style the app, so having a basic knowledge of tailwind would be good.

// routes/todo.tsx

/** @jsx h */
import { h } from "preact";
import { tw } from "@twind";

export default function Todo() {
    return (
        <div class={tw`w-screen h-screen flex flex-col justify-center items-center`}>
            <h1>hello world</h1>
        </div>
    )
}

This syntax is very similar to react as we are using jsx, tw is being used to style the elements using twind, you can learn more about it from twind's site if you want to. Now, if you did everything correctly, going to localhost:8000/todo will give you a page which looks like this -

ss2

Now, let's start by building our todo component inside the islands/ folder. Create a new file named TodoComponent.tsx inside inslands folder and put the following code inside it.

// islands/TodoComponent.tsx

/** @jsx h */
import { h } from "preact";
import { useState } from "preact/hooks";
import { IS_BROWSER } from "$fresh/runtime.ts";
import { tw } from "@twind";

export default function TodoComponent() {
  const [todoEl, setTodoEL] = useState("");
  const [todos, setTodos] = useState([]);
  const btn = tw
    `px-2 py-1 border-gray-200 border-2 hover:bg-gray-200 focus:outline-none`;

  return (
    <div class={tw`h-2/3 w-1/2 flex flex-col justify-center ites-center gap-3`}>
      <div class={tw`flex gap-3 h-[10%] w-full`}>
        <input
          type="text"
          class={tw
            `flex-grow-1 outline-none focus:outline-none border-gray-200 border-2 p-2`}
          placeholder="Enter new ToDo"
          onChange={(e: any) => {
            setTodoEL(e.target.value);
          }}
        >
        </input>
        <button
          class={btn}
          onClick={() => {
            if (todoEl) {
              setTodos([...todos, todoEl]);
              setTodoEL("");
            }
          }}
          disabled={!IS_BROWSER}
        >
          โž•
        </button>
      </div>
      <ul class={tw`flex flex-col gap-2 overflow-y-scroll min-h-[90%]`}>
        {todos.map((todo, index) => (
          <li class={tw`flex gap-2`} key={todo}>
            <p class={tw`flex-grow-1`}>{todo}</p>
            <button
              class={btn}
              onClick={() => {
                setTodos(todos.filter((todo, i) => i !== index));
              }}
              disabled={!IS_BROWSER}
            >
              โŒ
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

It's a basic todo app code, which you can understand easily. We have 2 states one for the current todo element and other for our list of todos, we render a flex container with two containers inside it, the first one has an input box and a button to add todos. We are using onChange on input element to update our todoEl state and a onClick in the add todo button which adds the todoEl to the array after making sure it's not null. The second part has a ul element which maps our todos array to create li elements with todo as their text and a button to remove the todo element using the index of todo.

Now we need to add this island to our todo route. We can do that like this --

routes/todo.tsx

/** @jsx h */
import { h } from "preact";
import { tw } from "@twind";
import TodoComponent from "../islands/TodoComponent.tsx";

export default function Todo() {
  return (
    <div
      class={tw`w-screen h-screen flex flex-col justify-center items-center`}
    >
      <TodoComponent />
    </div>
  );
}

Now, if you open localhost:8000/todo you'll see something like this if you followed the tutorial correctly-

ss3

You can try playing with the app to see if it works, and it will work! You can also try to add a button for marking the todo as done if you want to as an exercise.

This was a basic intro about fresh framework, you can learn more about it by reading the docs.

Thanks for reading, Happy Coding!

Buy me a pizza ๐Ÿ•

ย