How to handle prerendering with Purescript/Halogen

I’m currently working on making my site SEO and share friendly so that correct meta tags appear on each page. The easy way of doing this without prerendering is simply to have fixed meta tags for all pages and not define the og:url property. This will make e.g. facebook complain but it will share the site with correct url. However I would much rather be able to have dynamic meta tags on each page, different og_image based on the featured image of that page etc. But there is a problem.
In my Halogen application I use hash based routing meaning that express is only really serving the root / and then Purescript is handling navigation with hash based routing. It seems that prerenderers only support hash based routing if the route is defined as such


however Purescript Routing Duplex does not support this and defaults to the root page if I add the bang.

So my question is, has anyone dealt with having dynamic meta tags on big websites with Purescript?
changing these values dynamically with Purescript is possible but usually these sharing services cannot run javascript so they need to rely on the HTML being rendered by the server.


Working on it

Using my own version of loader

Todo: hydration and split pages js and preload when link to the page is on viewport (using Intersection Observer API)

1 Like

hey @srghma really nice to see this being worked on. Let me know if you need a tester on this.

Ok, I will tell when I have something)

Perhaps this should be considered a bug with purescript-routing-duplex.

Did you try this?

route = root $ sum
  { "Root": "#!"
  , "Page": "#!" / "page"

Hi @paulyoung I just made a key discovery from your reply. I don’t actually need it to be hash based so if I skip the root part and simply do

route = sum
  { "Post" : "posts" / slug

I can now access it on /posts/slug. Prerender should now be able to read this (will confirm).
I had to make a few changes. For example now in the server part I have this in express

app :: App
app = do
  get "*" $ render "index" ""

To route all requests to the same html template. I also switched out all hash based logic so now I use listen from PushStateInterface defined in Routing.PushState to listen for push changes to and not hash.
I think I can work with this for now. Thanks for your reply!

UPDATE: This very much works as expected and can cache the pages so the correct meta tags appear now when sharing

1 Like

I wrote a short blog post about this here: