"elm-starter", a tool for the Modern Web
Tags: webdev, elm, javascript, pwa
Published: 2020-07-26 08:56:43

Lately there has been some interesting discussion about **Modern Web**.

I personally think that

* **Single Page Applications (SPAs) have been (ab)used** in places where a more traditional server side approach could have been a better fit
* **SPAs require extra care** (navigation, scrolling, accessibility, SEO, etc.)
* **There are many SPAs that are not well crafted** but when they are well made, **SPAs can very much enhance the user experience**
* **The browser feels like the natural place where to handle complex UIs**, not the Server
* As developers, **I (we?) like to keep using my best tools** to improve the Developer Experience

Related to the last point I feel that the Elm language come with a *curse*:

> "*Once you are in Elm, you want to stay in Elm*"

This is not happening only with Elm, but with any compiled-strictyly-typed-inferred-immutable-purely-functional-with-nice-errors language.

So to alleviate some of these issues I made **a small script to convert Elm SPAs into Statically Generated Progressive Web Applications (PWAs)**, called `elm-starter`.

(Yes, [another one](https://www.staticgen.com/))

<hr />

#Goal

The goal is to have a tool that

* Is mainly written in Elm (see the *curse* mentioned above)
* Supports the majority of Elm libraries ([elm-ui](https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/), [elm-spa](https://www.elm-spa.dev/), etc.)
* Relatively simple to use
* Can be used to bootstrap a new app or to convert an existing one
* Is not invasive and can be removed at any time

<hr />

The resulting PWA should have these characteristics

* Works without Javascript
* Works off-line
* Is installable on mobile/desktop
* Is friendly to bots (SEO)
* Supports preview cards
* Renders fast (gets high scores in Lighthouse)

The classic Lighthouse screenshot with confetti 🎉

![The classic Lighthouse screenshot with confetti](https://dev-to-uploads.s3.amazonaws.com/i/4d2m2ehvta57qgy330a7.png)
<figcaption>High scores with Lighthouse. Well, it is an almost empty app, but still...</figcaption>

Preview cards in Slack:

![Preview cards in Slack](https://dev-to-uploads.s3.amazonaws.com/i/996uq77f0vylfro3nflc.png)
<figcaption>Preview cards in Slack. Different routes have different previews</figcaption>



<hr />

# What does this tool do?

* Generates all the needed files for a PWA
* Centralize in one "single-source-of-truth" all metadata
* Generates static pages

This is all. Quite simple!

# Demos

There are 3 demos made with `elm-starter`:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/j70x0wopf2eodsl1eily.png)
<figcaption>The 3 demos made using `elm-starter`, seen here in their installed version on macOS.</figcaption>

### 1. The *official* `elm-starter` demo

* [Demo](https://elm-starter.guupa.com/)

This is a simple SPA with an SVG object. It is an example of one of the differences between SPA and MPA: page transition. This app works both with and without Javascript. The only difference is that: No Javascript -> No page transition.

Note that the page transition is made with CSS transition applied to SVG. I realized too late that this is only supported in Chrome and I didn't have the time to change it, but you get the picture.

Here is the difference between having Javascript ON or OFF:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/zh48n0mi9emt2bjofwbk.gif)
<figcaption>The first series of clicks is with Javascript enabled, the other clicks are with Javascript disabled.</figcaption>

### 2. Todo MVC

* [Demo](https://elm-todomvc.guupa.com/)

This is the classic Todo MVC, the one that [I used extensively to compare different frameworks in a past article](https://dev.to/lucamug/resilience-to-errors-of-front-end-frameworks-3hej).

It was originally coded by Evan Czaplicki, I just adjusted it to fix some accessibility issues.

Compared to the previous demo, this one doesn't work without Javascript for its nature.

The preview card on Slack:

![The preview on Slack](https://dev-to-uploads.s3.amazonaws.com/i/nwqomriylbazby5byyuh.png)


### 3. `elm-spa` Example

* [Demo](https://elm-spa-example.guupa.com/)

This example shows a possible synergy between elm-spa, a nice way to create complex SPAs that take care of several things for you through automatic Elm code generation. For example, to create new pages you simply run a command in the terminal and this creates the right files, routes and logic for you.

`elm-starter` can then take over and transform it in a PWA, statically generating all routes.

In this specific example all pages are accessible without Javascript, but the widget that requires Javascript (like the counters) will not be working.

The preview card on Slack:

![The preview card on Slack](https://dev-to-uploads.s3.amazonaws.com/i/rxcthogca2rm0p70y6wf.png)


# Behind the scenes

* All files are generated by Elm code:

  * **HTML**: Generated using a modified version of [zwilias/elm-html-string](https://package.elm-lang.org/packages/zwilias/elm-html-string/latest/). For example [Index.elm](https://github.com/lucamug/elm-starter/blob/master/src/Index.elm) generates `index.html`.

  * **JSON**: Generated using [elm/json](https://package.elm-lang.org/packages/elm/json/latest/). For example [Manifest.elm](https://github.com/lucamug/elm-starter/blob/master/src-elm-starter/Starter/Manifest.elm) generates `manifest.json`.

  * **CSS**: Generated from strings. I know, this is not ideal. [rtfeldman/elm-css](https://package.elm-lang.org/packages/rtfeldman/elm-css/latest/), that is CSS with the type-safety of Elm, seems not exposing the `toString` converter. The quantity of CSS necessary in `elm-starter` is very small anyway so I kept it as a string. For example [SnippetCss.elm](https://github.com/lucamug/elm-starter/blob/master/src-elm-starter/Starter/SnippetCss.elm).

  * **Javascript**: Generated from strings. Again, the quantity of Javascript needed to run the App is minimal so I made the trade-off of saving it as string. For a larger amount of Javascript it is better to keep it in `.js` files. For example [SnippetJavascript.elm](https://github.com/lucamug/elm-starter/blob/master/src-elm-starter/Starter/SnippetJavascript.elm).

* All static pages and snapshots are generated using [Puppeteer](https://pptr.dev/).

* Most of the CSS and Javascript is formatted "[comma-leading](https://dev.to/tao/the-case-for-comma-leading-lists-3n49)" or "semicolon-leading" (sorry, I could not resist, see the Elm *curse* above, again)

*This is a snapshot of [Index.elm](https://github.com/lucamug/elm-starter/blob/master/src/Index.elm) that generates `index.html`:*

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ho03fx1klogrx4ssrq81.png)

# SEO

Each page is statically generated with specific meta-tags so that search engine spiders can easily pick it up, also when they are not as sophisticated as Google, that is [able to execute Javascript since a long time ago](https://medium.com/@l.mugnaini/spa-and-seo-is-googlebot-able-to-render-a-single-page-application-1f74e706ab11).

Someone says that even if Google can run Javascript, it still prefers static websites.

This is how Google indexed the `elm-starter` demo after few days from its launch:

![Google indexing the elm-starter demo](https://dev-to-uploads.s3.amazonaws.com/i/y9rpwi5hcgxix4jq4y1i.png)

<figcaption>Google indexing the elm-starter demo</figcaption>

# Similar projects

These are other excellent projects that can be used to bootstrap an Elm application or to generate a static site:

* [elm-pages](https://package.elm-lang.org/packages/dillonkearns/elm-pages/latest/)
* [elmstatic](https://github.com/alexkorban/elmstatic)
* [elm-spa](https://package.elm-lang.org/packages/ryannhg/elm-spa/latest/)
* [create-elm-app](https://github.com/halfzebra/create-elm-app)
* [spades](https://github.com/rogeriochaves/spades)


# About Modern Web vs. Traditional Web

To get more insights about the debate mentioned at the beginning of this post:

* [The disadvantages of single page applications](https://adamsilver.io/articles/the-disadvantages-of-single-page-applications/) by Adam Silver
* [Developing a Javascript-free frontend](https://javascript.works-hub.com/learn/a-javascript-free-frontend-61275) by Matt Reyer
* [The Return of the 90s Web](https://mxb.dev/blog/the-return-of-the-90s-web/) by Max Böck
* [Server-Side Rendering is a Thiel Truth](https://timr.co/server-side-rendering-is-a-thiel-truth) by Tim Ruffles
* [The Architecture No One Needs](https://www.gregnavis.com/articles/the-architecture-no-one-needs.html) by Greg Navis
* [Why I hate your Single Page App](https://www.freecodecamp.org/news/why-i-hate-your-single-page-app-f08bb4ff9134/) by Stefan Tilkov
* [Second-guessing the modern web](https://macwright.com/2020/05/10/spa-fatigue.html) by Tom MacWright
* [In defense of the modern web](https://dev.to/richharris/in-defense-of-the-modern-web-2nia) by Rich Harris
* [“The Modern Web”](https://css-tricks.com/the-modern-web/) by Chris Coyier

# How to use `elm-starter`

You can find `elm-starter` at [github.com/lucamug/elm-starter](https://github.com/lucamug/elm-starter).

The fastest way to start using it is by clicking this button:

[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/lucamug/elm-starter)

This will automatically

  * Create a repository containing `elm-starter` into your Github account
  * Deploy it live using your Netlify account

To modify the app simply clone the new repo on your computer and run:

```
$ npm install
$ npm start
```

Now you can edit `src/Main.elm` and, after confirming the result at http://localhost:8000, commit your modifications to Github.

The new app will be published live automatically by Netlify in seconds, including re-generating all static pages.

More information available in the [README](https://github.com/lucamug/elm-starter).

<hr />

This is all, thank you for reading! ❤️

<hr />

Cover image: Mount Fuji seen from Kawaguchi lake by [WorldInMyEyes](https://pixabay.com/photos/web-water-lake-mood-waters-rest-5348016/) from Pixabay