Building this site with Fresh, Deno and Tailwind

A Deno dino sipping on a Fresh glass of juice

For the past few years I've worked primarily with React, Typescript and Node.js, but when it came time to create this site Fresh caught my eye.

Fresh

Fresh is a lightweight frontend framework based on Deno. It uses a server side rendering model with 'hydrated islands' of interactivity. The big advantage of this model is that it allows you to ship very lightweight pages with no JS by default, whilst still allowing full JS based interactivity when needed.

Pages are built with Preact components so it feels pretty familiar coming from React, but that does make the odd difference which crops up a bit jarring. Fresh is probably most similar to Next.js when coming from React, but sadly I don't have much experience with Next to be able to compare them.

Overall I really like Fresh, it's very quick to get started with thanks to how integrated everything is and being based on Deno there's no faffing around with webpack, babel, etc which is a big plus! Every tech has its drawbacks though and I did hit a few rough edges.

One thing which caught me out was that I was thinking of islands as being entirely on the frontend, however it turns out they are rendered on the server side for initial load. This makes sense as a default but was problematic for me since I wanted to use a browser API which isn't available in Deno. I was hoping there would be a way to render a blank component from the backend and then re-render in the client but I couldn't seem to get that working.

The other part I'm not a huge fan of is the file based routing approach used in Fresh. Taking this blog as an example, I wanted a path of https://dleyland.com/blog/<blog_name>.

To achieve that in fresh you create a folder structure of:

routes: 
  blog: 
    [name].tsx

where the value in square brackets is the name of the URL parameter you can use in your route. Personally I navigate codebases by searching for file names (e.g ctrl/cmd + p in VSCode) which leads me to strongly favour unique, descriptive names. As you can probably guess these two traits don't really work well together and it was a constant source of friction for me while building this site. I would love to see Fresh add an option to allow defining param names in the route file rather than with naming conventions.

Deno

Deno is a new JavaScript runtime built in Rust which aims to replace Node as the de-facto way to run JavaScript on the backend. You can really feel Rust's influence in their approach to tooling which makes the developer experience great with things like a test runner and Typescript support already built in.

Annoyingly though at the time of writing there is no way to debug tests in the Deno VSCode extension. There is an awkward way to run a test in debug mode by creating a custom launch.json, but it doesn't appear to support having multiple tests defined (at least according to this Stack Overflow post).

The other wrinkle with Deno has been dependencies. Deno started off not supporting NPM packages, but then changed their mind later on and this has led things to feel a bit disjointed. Most NPM packages seem to work ok but then some like SQLite3 need a special Deno version to work properly and it's not always clear which is going to be problematic.

Tailwind

Currently Fresh only supports Tailwind as an option for styling, which was another new library for me. It essentially defines a set of CSS utility classes which you apply by setting the class property on elements.

An example of styling with Tailwind, taken from their website

Tailwind has been fairly easy to pick up, well documented and although a bit messy at times it's a solid choice for any frontend project (especially those which are on the simpler side). Having said that I think I will still default to styled-components for projects where that's an option. Mainly this is due to the extra layer of abstraction Tailwind introduces, which causes a few problems:

Learning Reinforcement

Historically I've been a generalist when it comes to tech which I love for keeping things interesting and being able to muck in with whatever needs doing. However because there's so much ground to cover that does make it much more important that I'm reinforcing knowledge which I'll need again in future when working with a technology.

With Tailwind I feel like I'm learning utility class names which will become obsolete in a matter of years, whereas writing styled-components helps to reinforce my CSS knowledge whilst still giving all the great benefits of modularity and specificity by default.

Translating Stack Overflow

Given the breadth of the CSS spec it's not uncommon to look up how to do a particular bit of styling. Normally this isn't an issue given how much information is available on sites like Stack Overflow, but generally solutions are going to be in terms of CSS rather than Tailwind.

This introduces an extra step of taking the solution you've found and trying to figure out which utility classes adjust each of the CSS properties you need to use. This isn't exactly a huge problem as the class names are generally pretty close, but it's an unnecessary bit of extra friction in the dev process.

One improvement I'd love to see Tailwind make here is to set up an index of CSS property -> utility class so that you could search them up directly on the docs site.

Conclusion

My main takeaway from this exercise is to be really intentional when starting a side-project about whether you're focussing on getting the project completed or whether it's for learning and exploration. I'm so used to using side projects to learn that picking up new tech for this site felt totally natural, but in retrospect I would have preferred to stick to what I know and get the site built a bit faster.

Overall I have pretty high hopes for where Fresh and Deno are headed, but both feel not quite ready for prime-time when it comes to production systems. Having said that if you're looking to try something new for a side-project then definitely give Fresh a go!