What is Fresh? - First Look at the Framework for Deno
Fresh markets itself as a next-gen web framework built on top of Deno that tries to stand out in the JavaScript ecosystem with a couple of cool features. This includes things like no configuration, TypeScript support out of the box, just-in-time rendering, and two highlights: no build step and taking advantage of the islands architecture, also known as partial hydration.
Components here are represented as islands, and JavaScript is only shipped for components that require interactivity. As Fresh is an SSR framework, it serves static HTML to the client by default with no JavaScript. This makes it very slim and ensures your site loads as fast as possible. This approach is very similar to how Astro works.
To make things interactive, you have to opt-in for using JavaScript by defining a component as an island, which we will take a look at later on how it can be done. But first, let's take a look at how to set up a fresh project.
How to Get Started
To get started with Fresh, you will need to have the Deno CLI installed on your machine. If you don't have Deno installed, you can get up and running by using one of the following commands:
Platform | Command |
---|---|
Shell | curl -fsSL https://deno.land/install.sh | sh |
PowerShell | iwr https://deno.land/install.ps1 -useb | iex |
Homebrew | brew install deno |
Chocolatey | choco install deno |
Scoop | scoop install deno |
Cargo | cargo install deno --locked |
Once you have Deno ready, you can create a new project by running the following command in your terminal:
deno run -A -r https://fresh.deno.dev <project-folder-name>
You can also opt-in to install twind
to be used for CSS during the installation process (a tailwind-to-JS solution). Then cd
into your directory, and run deno task start
to start the development server. You will be able to see the project running on localhost:8000.
The Project Structure
If you open up your newly created project, you will be greeted with the following folder structure:
We have two main folders where most of the work can be done in Fresh applications: islands
and routes
. Any component that you create in the islands
folder will ship JavaScript to the browser. If you open up the default component created for the starter project, you will see that each component needs to export a default Preact component from the file. So as you can see, Fresh uses Preact by default.
Note that Fresh uses PascalCase for the naming when it comes to components in the islands directory.
Routing in Fresh
Next to the islands
directory, we also have routes
. Fresh also supports a file-based routing, which means that your routes will be created in accordance with the layout of your routes
directory. With the starter project, we have an index.tsx
and [name].tsx
components.
Using brackets, you can create dynamic routes, and grab the URL values with props inside the component by referencing the name of the file:
/** @jsx h */
import { h } from 'preact'
import { PageProps } from '$fresh/server.ts'
export default function Greet(props: PageProps) {
return <div>Hello {props.params.name}</div>
}
Need to work with nested routes? Just create a new folder and put your components inside it. Just like for the islands
directory, a route also needs to export a default component. You can also modify the response of a route by exporting a handler
object from the same file:
import { HandlerContext, Handlers } from '$fresh/server.ts'
export const handler: Handlers = {
async GET(request, ctx: HandlerContext) {
const response = await ctx.render()
response.headers.set('X-Custom-Header', 'π')
return response
}
}
You can use different HTTP methods by naming the async
function accordingly where you have access to both the request and the response objects.
How to Fetch Data
Another common use case is working with data. Fetching data in Fresh looks very similar to the previous example. We need a handler again, but instead of modifying the response, we can simply return the render
call with the passed data:
interface Fetch {
data: number[]
}
export const handler: Handlers = {
async GET(req, ctx) {
// Here you can query your DB and return the necessary data
const data = [1, 2, 3]
return ctx.render(data)
}
}
export default function Home(props: PageProps<Fetch>) {
return props.data
}
This will be accessible inside the component under props.data
. Or you can pass an object, in which case, the nodes will be accessible under props.data
again:
export const handler: Handlers = {
async GET(req, ctx) {
return ctx.render({
user: {
name: 'John'
}
})
}
}
export default function Home(props: PageProps<Fetch>) {
return props.data.user.name
}
Summary
While Fresh looks really promising on the surface, it is still relatively new so you likely don't want to use it in a production environment just yet. One major drawback is the lack of community. NPM still has a much larger userbase than Deno, and some NPM packages may not be compatible with Fresh yet.
If you would like to find out more about Fresh, you can head over to fresh.deno.dev for the full documentation. Have you already used Fresh before? Leave us your thoughts in the comments below! Thank you for reading through, happy coding!
Rocket Launch Your Career
Speed up your learning progress with our mentorship program. Join as a mentee to unlock the full potential of Webtips and get a personalized learning experience by experts to master the following frontend technologies: