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
This will start your app on localhost:8000, with a basic counter app.
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 -
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-
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!
Top comments (41)
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!
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.
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.
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.
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
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.
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.
thanks!
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.
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.
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
Install the official deno extension for vsc.
create a
.vscode
folder in the project dir withsettings.json
file, put the following content inside it -this is the basic config, you can learn more here.
If you have docker installed, use the official deno devcontainer, it has everything set up
Thank you for sharing!
Can we use preact-router with it ?
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
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!
its a relatively new tech, I'm currently trying to learn more about it myself so no need to worry π
Thanks for the write up. This looks like a cool framework
Great article, thanks for sharing
Thanks for the post Ashish. Nice way of a starter introduction to Fresh. Once again, thanks.
thanks for reading!