Introducing elm-spa– version 5!

Hey everyone! :wave:

For a while now, I’ve been working on elm-spa: a project to help make building Elm applications easier. I just published the latest version, and I wanted to share it here!

Here’s the newly designed website (built with elm-spa)!

:brain: The Big Idea

The big idea behind elm-spa is to let folks focus on adding features, while it generates all the tedious stuff.

  • Automatically generates top-level page-wiring and routing code.
  • Automatically parses URLs based on file naming convention.
  • Brings tools like elm-live, elm-test, and elm-ui together.

:woman_technologist:t2: An Improved CLI

Although elm-spa still has only three commands (init, add, and build), they have been updated with an interactive experience. This makes creating projects and adding pages a little nicer.

:heart: The Best Community Ever

I want to personally thank @evancz , @avh4, and @dillonkearns for taking time to talk through API design with me– their advice led me to a much nicer solution!

I also watched (and rewatched) a lot of incredible talks from @rtfeldman, which helped me keep the overall design simple (avoid the elm-fork-knife!:fork_and_knife:)

Special shout-out to the amazing folks in the #elm-spa-users channel on Slack for their helpful feedback and constant encouragement.

:face_with_monocle: Share Those Feels

I’d love to hear your thoughts on the latest API and the official guide.

Get started with one command:

npx elm-spa init

And run your elm-spa project at http://localhost:8000

cd (your-folder)
npm start

And here’s the GitHub repo– if you’d like to see the source code!

Hooray for new things! :tada:


Amazing job @RyanNHG! Thanks for all the effort you are putting into this amazing library :raised_hands:t3:


Nice work @RyanNHG :clap: :clap: :tada:

Just one question - I have an app based on v4 - is there an easy way to upgrade from v4 to v5?

Thanks, @kraklin! :relieved:

The biggest API change for upgrading from v4 is how pages make updates to Shared.Model (previously called Global.Model). The Elm compiler should be able to guide you through most of the refactor. I’ll explore this on my personal projects running 4.x!

If you attempt to do this– it would be great to share your experience in #elm-spa-users– and I’d love to help out in case you run into any specific problems.

1 Like

Sure, I’ll definitely try it out :wink: My project is like super simple, just two pages, one of them dynamic + I’m not using Global at all IIRC, so it should be quite smooth. I’ll let you know :wink:

1 Like

What are some of the differences between elm-spa, and elm-pages?

@maxthegeek1 – great question! They are both similar, but have different goals.

elm-pages is a static site generator, to make static blogs or other websites.
elm-spa is an application framework, to help you build any web application.

You can build static blogs or websites with elm-spa, but elm-pages has a lot of features tailored specifically to that problem space!

In the React world, an analogy might be Next vs Gatsby
In the Ruby world, another analogy could be Rails vs Jekyll


So I’m done upgrading from v4. My short How to upgrade guide:

  • I have started by creating the whole app with elm-spa version 5 elm-spa init next to my current app.
  • Then I elm-spa add the scaffolding for the pages I already have in my old app version
  • Then I started to copy/paste the whole parts of pages including init/update/view functions into the scaffolded structure and I have followed the compiler errors on what he is missing.
  • In the process I have to create the navigation utility module to handle page programatic navigation + recreate the part of shared Global.Model into the Shared.Model and later I needed to pass the data from that model into the pages themselves.
  • And once it all compiles… it really works - this is still my most favourite part of whole Elm experience

@RyanNHG - what would be helpful for me:

  • some code example of already existing app - e.g. the main elm-spa page - I was lost for a while around the whole change of concept of Global -> Shared module and the work with the Global.Model
  • I was unable to rewrite easily static Page type even though I thought I had the types right. The problem was that Page.static is Page Params Model Msg and the view there is view: Url Params -> Document Msg . I had to scaffold the static page to see that I have missed that the Model is in fact Url Params there (I was having it empty as default from v4 and I was getting either problems with page or view annotation there)

Other than that everything went really smoothly and I really love the new simplified api of Pages. Well Done


(putting this here for anyone who would find it helpful because in the elm-slack channel it will be lost in the history)


Hi there!
I’m absolute newbie here.
Can Elm provide dynamic component add/removal from a page?

1 Like

Elm is a compiled language, so you probably can’t easily add something at run time. However, you do things like conditionally display something based on the application state.

What use case are you thinking of?

1 Like

@Andrea_Scotti welcome to the community!

Just like JavaScript frameworks, Elm can add and remove components dynamically from the screen:

if isSignedIn then

I recommend visiting the official guide to get a quick tour of the language, it has beginner friendly guides that helped me when I got started:

If you have more questions, the amazing folks in the #beginners channel of the Elm Slack are available to help answer them:

Thanks for saying hello here! :grinning:

Thanks Ryan!
Greetings :slight_smile:

Hey @RyanNHG, great work on this, I really like the direction you’ve taken. :clap:

I’ve just finished porting my app from v3 -> v5. It’s required some pretty big changes to the architecture of my application, but it has definitely been worth updating. I really like the save/load model for syncing shared state, it is very intuitive and really simplifies a lot of my code. The default code that replaces ports.js is also much easier to work with and a bit less opinionated, which I like. But the most successful aspect of the new version is that the code just looks a lot more like a standard Elm app, it’s just extended a bit, which makes it so much more familiar and should make it much easier for people to start using elm-spa.

I had a couple of thoughts while I was upgrading:

  1. The new site is lovely and I really like that it recommends some best practices that aren’t really specific to elm-spa, but at a couple of points I was thinking “tell me what to type to make it work!” It was specifically related to adding new pages, I kept on trying to type something like “Designs/Uuid_String”, despite it telling me to use “Elm module syntax”, I got fixated on adding a new endpoint so typed a URL. This might have just been me, but giving an explicit example of the format of the command in the docs or on the CLI would help boneheads like me!
  2. I miss “global commands” a bit. I don’t want them in the update, but being able to send a global command from save would be really handy. So it would mirror load:

    save: Model -> Shared.Model -> (Shared.Model, Cmd Shared.Msg)

    I wanted to use this to trigger app state in Shared.Model to be saved to local storage, but I didn’t want to add all the relevant fields from Shared.Model to my page’s Model. I ended up with a work around using a subscription. I’m sure there’s a reason you didn’t so this, so let me know if there’s a better way.
  3. The CLI has some quite dark font, which I couldn’t see at first. Not sure if that was specific to my terminal config!

Once again, you’ve done amazing work on this. elm-spa quickly becoming another great reason to use Elm.



This is awesome feedback, thank you so much for taking the time to post that here :heart:

Sending Cmd Shared.Msg from pages was a powerful thing, although I don’t miss the weird triplets from version 3.x haha

There are some side effects of that not allowing pages to send shared commands that I really like:

  • All Shared messages can only be called from Shared.view
  • Doesn’t encourage folks to expose Msg(…) from the Shared file
  • Avoids that send : msg -> Cmd msg function altogether

I definitely like the visual symmetry of having save and load both return Model/Cmd tuples. But because save takes in Model, I feel like it will lead folks to store things like model.sendMyCmd on update and trigger commands in strange ways.

For your example: If you’d like to save to local storage after every shared update, I’d do that in Main.elm. Although it’s rarely changed, updating Main.elm is one fo the benefits of upgrading from 3.x! That’s a great place for controlling top-level behavior like that.

I create one Ports.elm file for all my ports, and call Ports.saveToLocalStorage from within Main.elm

Again, thanks for the feedback– I sincerely appreciate it! :grinning:


Perfect, this is exactly what I was looking for, it makes total sense to put this type of behavior in Main.elm. I’ve only looked at that file once before and I had totally forgot about it. I definitely don’t miss the triplets either, it’s much cleaner now.

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.