Hello.
How to build SPA with Roc? I was using Elm previously.
I have found this for Elm programmers but can't find something about SPA https://github.com/roc-lang/roc/blob/main/roc-for-elm-programmers.md.
I see that I can generate static website
I see there are platforms but no web platform? https://github.com/zraexy/roc/tree/main/examples/platform-switching
There is this project https://github.com/roc-lang/roc/tree/main/examples/virtual-dom-wip but I think it is going webassembly way and is also WIP.
Thanks.
We don't have any SPA platforms yet but you can use Roc for the backend with the basic-webserver platform.
Roc doesn't compile to JavaScript, and the plan is that it won't. So in the browser, WebAssembly is the one way to run Roc!
I've had fun writing a web app using htmx. I wrote about my exploration https://lukewilliamboswell.github.io/roc-htmx-demo/
Richard Feldman said:
Roc doesn't compile to JavaScript, and the plan is that it won't. So in the browser, WebAssembly is the one way to run Roc!
What would it take to modify the compiler to emit javascript instead of, say, llvm?
The readme (https://github.com/roc-lang/roc/blob/main/crates/compiler/README.md) lists a number of steps. Where, and what kind of changes are need to allow targeting javascript (or any other scripting lang)?
How to implement platforms when compiling to javascript is a different question. My main interest here is if it is possible at all, and if so how, too parse + convert Roc to javascript, turning Roc into a viable replacement for Elm
it's an explicit non-goal of the project to emit JavaScript, but if you wanted to try it yourself on a fork, the minimal change you'd need to make would be to add a new gen_
crate (like gen_llvm
or gen_dev
) which would go from mono::ir
to whatever output you wanted
A better (or maybe just easier) way to use Roc in the JS world would probably be for someone to create a platform that compiles to wasm and provides access to parts of the JS stdlib. Then you would load the wasm file in JS and hook up the wasm calls to JS calls. It would be weird but i think it would work, right?
right, in general that's how wasm in the browser works :big_smile:
the wasm itself is basically only capable of doing calculations, so wiring it up to some amount of hardcoded JS is necessary to be able to do any interesting browser stuff
Great! So someone just needs to write that hard coded JS file that exposes all of the effects in the JS stdlib and that's basically the same as compiling to JS :sweat_smile:
Something I've always seen handwaved in wasm "code sharing" or the like is referential integrity. A lot of JavaScript optimizations rely on it and I worry that dropping a wasm bundle in would prevent any sort of "share you business logic using a roc module that your existing code can use" migration path
The nice thing about compile to JS is that there is no to/from wasm conversion that breaks those optimization (obviously there are other pitfalls, but that's just one)
If you want JS compilation, why not just use Elm?
"share you business logic using a roc module that your existing code can use" migration path
Also, I'm not really sure how a wasm bundle stops this, can you explain more?
What is referential integrity? Is it related to referential transparency?
I've done a lot of webassembly work but I'm not following this point.
For sure, let's say that you have a mobile app and a web app and you want to share some logic in a common roc module (basically along the lines of kotlin multiplatform), elm won't run in a non-browser environment so I don't think that's an option.
Referential integrity is basical JavaScript's way of saying pointer comparisons. It is a common optimization to check object (essentially pointers to records) equality by just comparing the references themselves. If they reference the same object they are equal , or perhaps more importantly if they don't have the same reference you assume they are not equal
From what I understand of wasm, if I'm using any data inside of wasm from js-land then I have to marshall it across the wasm-boundary, and then if I return the data I would marshall it back across the boundary and end up with a different reference (and in this case most likely a deep copy), this would make it unusable in a lot of common js patterns.
For example selectors in redux rely heavily on referential integrity in order to avoid triggering re-renders, if the reference changes each frame then it can't avoid doing deep comparisons which are slow
The way most wasm-based web frameworks do stuff is they shift as much as possible into the wasm side so all that is ever marshalled across is the minimal DOM changes, no application data
This isn't a problem outside the browser because there doesn't have to be much marshalling to cross the C ffi. Presumably a platform could just "understand" a roc type and it's all good to go
Note: I should have used the term referential equality not integrity. Related concepts but not exactly the same, and probably confusing that I used the wrong one
I also think it is totally fine to say "swapping out a redux store in a react app for a roc module is not a use case we think is in scope". Just pointing out that there are reasons people like compile to js over wasm for some use cases
@Pearce Keesling have you seen the virtual dom example? That might be good to see some ideas for managing the WASM boundary and sharing logic on front and back ends.
Yeah, I think that is a great strategy for when a whole slice of your ui is going to be in roc. I don't think it avoids the problems when you're embedding just logic into a react app.
For example a real use case we have at my company. We have a list of transactions that's 10s of thousands of times long. If I want to update a single transaction I need to send it all across and then update a value and send it back. In order to use wasm I'd need to find a good "cutting point" to minimize the amount of serialize/deserialize I have to do. If it's just js I can plug it in way easier
Given how roc handles effects, I think it will always have some complex edges in this area. Fundamentally a good "cutting point" is required to use roc in general. That is what a platform hopefully is.
I do agree that wasm makes this more complex. For this cutting point, you might:
Any of these might mitigated the issue, but fundamentally, performance would need to be measured.
Absolutely, all of those are reasonable routes. It's all about tradeoffs
Luke Boswell said:
Pearce Keesling have you seen the virtual dom example? That might be good to see some ideas for managing the WASM boundary and sharing logic on front and back ends.
I think a really interesting concept is using this pattern for targets other than the browser
like in general if you have a Roc function whose purpose is to quickly return a description of how you want the UI to look, and then the platform's job is to translate that into actual visuals...
you could implement something in the browser which does that, sure, but in theory you could do the same in Swift on iOS, calling the Roc function via Swift's C FFI
and same thing on Android
same thing on desktop, etc.
I noticed that one of the open native devs is working on decoupling react native from react. I think I tagged @roc_lang in the tweet thread. That might be a super interesting foundation to build on since react native is essentially a C FFI for native views
@Pearce Keesling any chance you could dig out a link? that sounds interesting.
Also, somewhat related to the above.
I've been expanding on my roc+htmx demo, refactoring it and generally tinkering. I think it's a good sandpit for developing basic-webserver features and trying out ideas.
But I wonder if anyone else would be interested in tinkering with it also? I thought I might ask because if anyone would be interested in making PRs and generally adding random features then that might be fun.
There is no overall direction or anything, I'm just doing the next thing that pops into my head, a random exploration of different ideas for building web apps with Roc. To be honest, I have spent most of my time just rebuilding things I've already built and refactoring.
I want to challenge myself to write up a blog article every now and then to capture some of the things I learn along the way.
It's currently sitting in a private repository... I should probably make it public anyway.
Thoughts?
This is the tweet in particular but he's been posting a lot about it this week https://x.com/birch_js/status/1757743078738469145?s=20
Luke Boswell said:
I've been expanding on my roc+htmx demo, refactoring it and generally tinkering. I think it's a good sandpit for developing basic-webserver features and trying out ideas.
But I wonder if anyone else would be interested in tinkering with it also? I thought I might ask because if anyone would be interested in making PRs and generally adding random features then that might be fun.
...
Thoughts?
I've been wanting to learn htmx and expand my limited roc knowledge, so this is cool. I went ahead and created a PR with a feature to add a button to mark a task as completed. https://github.com/lukewilliamboswell/roc-htmx-playground/pull/1
If you don't want to merge it, I won't be offended. I'm just thankful that you created the playground and gave me a good spot to experiment.
Awesome :heart_eyes: I can have a closer look at this later. It's hard to read on my phone, but super cool. How did you find roc + htmx + sqlite? I think they're a pretty nice combination so far.
I find them pretty nice! There are some rough edges and oddities to roc for me still, but building the client side interaction in the element attributes feels good. Having a server side elm-like language is amazing, and the unexpected bumps are an indication of the promising future. You'd already done the hard work with the sqlite stuff, so I just had to write a query and use your existing connection, so I don't feel like I can comment on that too much, but it does seem like a good choice.
Sorry about the formatting changes in my PR. The roc formatter is set to auto format when I save. I probably should have tried to isolate that to a single commit, or turned it off. But I wasn't intending to make a PR when I started messing around, I just wanted to see if I could get it to run, and then wanted to see if I could add a feature. It was only when I'd finished that I thought I should make a PR out of it.
Last updated: Jul 06 2025 at 12:14 UTC