In this second post on Deno Fresh getting started, we see how Fresh islands of interactivity work. As well as that we look how you can use Deno’s in-built testing in your Fresh app.
In the first part of the series we saw how you can add web pages to your Fresh app using its file-based routing system. We extend on what we saw there, in this follow-up, to see how you can also add API routes as well as resource routes (serving an XML RSS feed, for example).
We started the first post looking at why you might consider Deno Fresh and saw how you can set up Deno on your system and spin up your first Fresh app. If you are new here, please do skim through the previous post if something here does not click.
We will start with a quick introduction to partial hydration and the Islands architecture. Then we will see how Deno implements it’s opt-in Island components philosophy. Then we move on to look at testing and API routes.
Deno Fresh is a fantastic choice for content sites. Here we are talking about
blog sites, documentation sites and such like. In the near-recent past statically generated sites
were the most popular choice for content sites, with something like GatsbyJS a common choice. The
reasoning behind choosing static generators over server-side rendered (SSR) ones were principally speed and security. There was a Developer Experience trade-off though, which lay in the fact that sites took long
to build (as the static content was generated). However, the user got the benefit when the static
content could be served quickly from a global CDN
Following on from the previous Getting Started with Deno Fresh post here are ten more Deno Fresh getting started tips.
For this to work, we put our component which have state or interactivity in the
islands directory of the project, rather than the
So returning to the social share buttons example, we can add that code to a
ShareButtons.tsx file, which looks just like any other React component:
Here, we are using the WebShare API with graceful degradation for devices which do not yet support it. To achieve that we:
useStatehook, assuming initially that the device supports the WebShare API,
useEffecthook to feature detect the WebShare API and update are support assumption is needed,
- show the WebShare icon when the API is supported but show manual Telegram and Twitter share button otherwise.
Here the opening
form tag includes a method attribute set to
POST as well as an action. The action can be the pathname for the current page, fed in to this component
as a prop.
When the form is submitted, the device now sends a
POST request to
the same route. We just need to add a
POST handler in the Fresh code
for this page (much like we had in the Tweet Queue Deno Fresh route file example).
You will have files like favicons and perhaps web manifests for PWAs
static folder. Deno will serve them for you from there. As an example,
static/favicon.ico will be served on your built site from
That’s not all Deno does for you though! There is a handy cache busting feature, even for these static assets! What it does is add a hash to the filename (under the hood). How is this helpful? If you change a favicon (lets say you go for a different colour) and you keep the same file name as before. Deno will recompute the file hash, this will be different to the previous one.
This means browsers and CDNs will know to download the new coloured favicon instead of serving the cached one. To make it work:
assetnamed import function from
when you include the favicon, image, CSS or other asset in a link tag, wrap it in a call to the
You can choose Tailwind for styling in the interactive prompts when you initialise
your project. If you prefer vanilla CSS then, of course, Fresh handles that too!
You can even self-host fonts! Add the CSS in the
static folder (like we mentioned for favicons above). Then just remember to include it in the
Head for your page or layout component markup (again just like for favicons):
If you do want to self-host the fonts, there is a handy Web Fonts helper
which generates the CSS and lets you download the
We mentioned earlier that there is no need to spend time configuring ESLint or Prettier in each Deno Fresh project you start. Deno comes with its own linter and formatter all with sensible defaults. To run these from the command line, just use:
To have VSCode format on save see the VSCode config in the quick tips in the previous Getting Started with Deno Fresh post. If you are a Vim person, try the denols LSP plugin for Neovim .
Nothing to see here. TypeScript support come out with Deno out-of-the-box: no need to add a
typescript-eslint config. Just start coding in TypeScript.
Just like linting and formatting, Deno has testing built in — there is
0 test runner config. That said, you might want to set up a test script, just to fire off tests
quicker. Update your
deno.json file to do this:
Then to rattle off tests, in the Terminal type:
If you have already used Jest or Vite, then there are no surprises when it comes to writing the
tests themselves. Import
assert and friends from
std/testing/asserts and you are already at the races!
You might use API
The actual API route file is essentially just a regular route file handler. You can name the
file with a
.ts extension in the API case though. Here is an example
where we send SMS message via the Twilio API from a Deno Fresh API route:
Although we put the file in an
api subdirectory this is by choice
and not necessary.
Notice the Deno way of Base64 encoding BasicAuth parameters. We import the
encoder in line
2, then use it in line
18 to generate Base64 string which we need to send in the Basic authorisation header in line
Response object. You can just as easily return a redirect or server error.
For convenience there are also
Response.json, providing a spot of syntactic sugar:
That last one adds extra convenience, saving you having to construct the customary headers manually.
You might use resource routes to serve PDF files, JSON data or even an RSS
Middleware or Edge Functions in Deno let you intercept incoming requests and run code snippets on them before proceeding. Again these are build into Deno Fresh. As well as intercepting the incoming request, you can effectively alter the response. See the video on Deno Fresh Middleware for more explanation on this.
_middleware.ts file to any folder containing route files you
want it to apply to:
Although the process is not complicated, we will not go into it here as there is an example with fully working code on using Rust WASM with Deno Fresh.
We have seen a lot in the last two Deno Fresh getting started articles. I have tried to integrate my own learnings and take you beyond what is in the Deno Fresh docs. I do hope this has been useful for you especially covering helpful details for anyone new to Deno. In particular, we have seen:
- how Deno Fresh islands work,
- examples of using the platform with Deno Fresh,
- an introduction to more advanced Deno Fresh features like WASM, API and resource routes.
Get in touch if you would like to see more content on Deno and Fresh. Let me know if indeed you have found the content useful or even if you have some possible improvements.
- Typically most pages on your site will serve HTML content. However, you might want to serve a PDF brochure or even an XML RSS feed. This is where resource or non-HTML routes come in. To add one to your Deno Fresh project, just create a TypeScript file your project `routes` directory. Remember this follows the file-based routing system. So put the handler function for your PDF brochure in `routes/brochure.pdf.ts` and this will be served on your production site at `https://example.com/brochure.pdf`. Write the handler, which looks a lot like the handler on an HTML route, returning an HTTP Response. Remeber to set appropriate headers for the data you serve (`Content-Type: application/json`, for example on a JSON route).
- For API routes, the handler will not look too different to what you have on a page route. There are a few things to remember though. Routing works just like for pages and the API route file can go in the routes directory. As an example create `routes/api/index.ts` to listen on the `https://example.com/api` route. You can add `GET`, `POST`, etc. handlers there. You will return a Response object instead of `context.render` (as you do for a page). Remember to add in any headers in the Response constructor. For example, on a `routes/api/data.json.ts` route, you will probably want to serve a `Content-Type: application/json` header.
Have you found the post useful? Would you prefer to see posts on another topic instead? Get in touch with ideas for new posts. Also if you like my writing style, get in touch if I can write some posts for your company site on a consultancy basis. Read on to find ways to get in touch, further below. If you want to support posts similar to this one and can spare a few dollars, euros or pounds, then please consider supporting me through Buy me a Coffee.
Aimed this new Deno Fresh post at anyone new to Deno wanting to try the 🍋 Fresh approach to partial hydration.— Rodney (@askRodney) January 20, 2023
You see static asset cache busting, in-built testing and a whole lot more.
Hope you find it useful!
#learndeno #usetheplatform #futureofweb https://t.co/JsERXYQHNf
Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via @askRodney on Twitter, @[email protected] on Mastodon and also the #rodney Element Matrix room. Also, see further ways to get in touch with Rodney Lab. I post regularly on Astro as well as Deno. Also subscribe to the newsletter to keep up-to-date with our latest projects.