DEV Community

Cover image for React is awesome but have you tried fresh?
ashish
ashish

Posted on • Updated on

React is awesome but have you tried fresh?

Why Fresh?

Let me 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.

Create the App

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
Enter fullscreen mode Exit fullscreen mode

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>
    )
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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.

Conclusion

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 🍕

Discussion (37)

Collapse
dao profile image
Dao Shen • Edited on

Kudos for using Deno! <3

React is not awesome. It encourages people to embed content and the structure for content in code. It looks like fresh is doing the same thing. No. For anything other than toys and one-offs, doing this results in brittle systems to maintain.

Reactive architecture however, now this is awesome. Really really awesome. Learn this from React and fresh, and then when we are done playing, we'll be ready for systems that both scale and can be maintained and grown over time.

All the best!

Collapse
asheeshh profile image
ashish Author

I agree with the second paragraph, couldn't have said it any better, but about the first paragraph - if you don't mind can you elaborate what do you mean by "embedding content in code", I think I'm missing something here.

Collapse
stealthmusic profile image
Jan Wedel

I think it’s about having logic, structure and styling in one file. I have never used react, but it always looked very weird to me. I agree with that in large applications, I would never ever want this. When I am changing code, I’ll do it in a ts file, when I change structure, I’ll do it in an html file and when I style something, I’ll do it in a css file. Very simple. That’s how browsers work and it’s a very sensible separation of concerns.

Thread Thread
asheeshh profile image
ashish Author

makes somewhat sense, though it's hard to structure your project when it's really big as most of the frameworks don't work the way you want them to and seeing how css-in-js is being used by more people, but I don't hate the way of structuring projects using folders like pages, components, styles too. It's not exactly what you want but is somewhat efficient and readable.

Thread Thread
imonem profile image
imonem

I think having everything in one file is for the purpose of the tutorial and not intended to be for scaling production. I use sass for larger projects. However if it's a quick prototype then it's bootstrap or two for the sake of speed

Thread Thread
asheeshh profile image
ashish Author

absolutely, I just wanted to show the basics of the framework, fresh is still very new to be used in production but I have high hopes from it.

Collapse
resetnak profile image
Alexandr Rešetňak

Just saw a video from Fireship about Fresh and there is already an article. Great community! Will definitely try Fresh. And well written article! Thanks.

Collapse
asheeshh profile image
ashish Author

thanks!

Collapse
n0n3br profile image
Rogério Luiz Aques de Amorim

I saw the documentation and wondered how can I share state between islands. I can't stick one island inside another island to pass the state by props. For me it seems a big downpoint for fresh. Hope they figure it out.

Collapse
asheeshh profile image
ashish Author

you can use non interactive "components" inside islands though, but not being able to call an island inside another is surely a thing I hope they work on.

Collapse
tqbit profile image
tq-bit

Fresh looks pretty cool. Out of curiousity: Could you find some tooling that helps with Deno imports? VSCode's intellisense alone doesn't cut it and shows me one error after the other when I try and fiddle around with Deno

Collapse
asheeshh profile image
ashish Author • Edited on
  1. Install the official deno extension for vsc.

  2. create a .vscode folder in the project dir with settings.json file, put the following content inside it -

{
  "deno.enable": true,
  "deno.lint": true
}
Enter fullscreen mode Exit fullscreen mode

this is the basic config, you can learn more here.

  1. for finding packages, use deno.land/x
Collapse
bherbruck profile image
bherbruck

If you have docker installed, use the official deno devcontainer, it has everything set up

Collapse
n1ckdm profile image
Nick

Great article, thanks for sharing

Collapse
green8888elephant profile image
Kostya

What about size of "deployment", because it doesn't have a build and you use preact.

Is there a lot of benefits?

Collapse
asheeshh profile image
ashish Author

I don't understand what you mean by "doesn't have a build", fresh does build the app, github.com/denoland/fresh/blob/mai...

about bundle size, I'm not really sure as I didn't check but it shouldn't be high according to me.

there are some benefits, being able to run the whole app on edge is a benefit in itself, not to mention all the benefits of using deno over node count in too, overall it's a great experience using fresh and I have a lot of expectations from this framework personally!

Collapse
green8888elephant profile image
Kostya

Ok, maybe I misunderstood. I will take a look) Even I didn't know about Deno a lot)

Thread Thread
asheeshh profile image
ashish Author

its a relatively new tech, I'm currently trying to learn more about it myself so no need to worry 👍

Collapse
andrewbaisden profile image
Andrew Baisden

Cool, this is a new one for me.

Collapse
yongchanghe profile image
Yongchang He

Thank you for sharing!

Collapse
akshaw profile image
Ayush Kumar Shaw

Thanks for the post Ashish. Nice way of a starter introduction to Fresh. Once again, thanks.

Collapse
asheeshh profile image
ashish Author

thanks for reading!

Collapse
largenty profile image
Largenty

Can we use preact-router with it ?

Collapse
asheeshh profile image
ashish Author

again, I haven't tried it myself so I'm not sure, you can try asking in the issues though.
github.com/denoland/fresh/issues

Collapse
funnypan profile image
funnyPan

how to import icons

Collapse
asheeshh profile image
ashish Author

As many npm modules dont yet support deno, things like react-icons may not work, though you can use simple-icons for sure with an image tag.

Other than that I'd personally prefer getting the svg paths of the icons and then exporting them as jsx components, for example. see this --
github.com/denoland/fresh/blob/mai...

Collapse
funnypan profile image
funnyPan

got it.
do u have try to deploy on vercel?

Thread Thread
asheeshh profile image
ashish Author

i didn't try it, i'd suggest deploying on deno deploy though, runs on edge and has faster deployment

Collapse
pyrsmk profile image
Aurélien Delogu

Is the page refreshed at each action? Like is it refreshed when you add a task in the to-do list?

Collapse
asheeshh profile image
ashish Author

no it doesnt

Collapse
pyrsmk profile image
Aurélien Delogu • Edited on

So there's JavaScript embedded inside the page, isn't it ?

Thread Thread
asheeshh profile image
ashish Author

yes i think, according to what i have read, fresh uses just in time rendering, and also uses partial hydration through islands, and i don't think refreshing on adding a new todo is an expected behavior as i'm not using forms, right?

Thread Thread
pyrsmk profile image
Aurélien Delogu

Mmmh ok. I read the documentation a bit. So it seems JS is sent to the client when islands are used, is that it ? Because without it I cannot see how all of this could work ahah.

But that's cool, Fresh seems really equivalent to Phoenix (an Elixir framework). Those hybrid frameworks are the future IMHO.

Thread Thread
asheeshh profile image
ashish Author

yes js is sent to the client when its needed, and not all components are hydrated, only the islands are hydrated i.e. provide interactivity

Collapse
bherbruck profile image
bherbruck

Did you get twind autocompletion working in vscode? I couldn't get the plugin working in a deno environment

Collapse
asheeshh profile image
ashish Author

someone in issues helped me out, its working perfectly fine now, see this: github.com/tw-in-js/vscode-twind-i...

Collapse
asheeshh profile image
ashish Author • Edited on

Doesn't work for me too, I have opened an issue in the repository regarding this, hopefully they'll fix it soon