Turbolinks with Phoenix

As promised a Phoenix, Turbolinks, React, Redux, and Immutable.js example. Redux and Immutable are not a requirement for this setup, but they make frontend work a tad more enjoyable for me :slight_smile:

source: https://github.com/nerdyworm/returb
demo: https://sleepy-shore-92135.herokuapp.com (disclaimer: user editable content on the internets)

Pain Points/Things I haven’t figured out:

  1. Mix of html, server side js, and json serialization are required to get everything working in a real app. Full SPA would only require json.
  2. I’d really like to override the redirect function instead of: https://github.com/nerdyworm/returb/blob/master/web/turbolinks.ex#L2
  3. Turbolinks caching and react components that change state cause some screen flickering. My current solution is to basically just bust the cache on state changes.
  4. Remote forms are a bit tricky to get right when react.js components are involved.
  5. Flash messages are not working, not sure why. Probably simple.
  6. You have to go all in with Turbolinks if you have any components that depend on global state. I actually have not run into this yet, but I can see it happening in a full featured app.

I do not have that much experience with React and elixir. I could be doing it totally wrong. If I am please help :wink:

If there are any questions just ping me and I’ll be happy to help.

Cheers,
Ben

9 Likes

Any one tried Morphdom? https://github.com/patrick-steele-idem/morphdom

Supposedly simple way to update parts of DOM preserving states like input cursor positions

As described in this well written blog post: https://0x2a.sh/replacing-react-with-rails-66e25cd23777#.yuwzpcj6m

It feels like I’m seeing more of the “replaced react with traditional rails/turbo-links” type of posts suggesting web-dev is coming round full circle:

Early 2000’s.
Full page refresh is the standard. Works great.
Sprinkle in a little js. Works good.
Add more js, jquery mess. Not so good.
SPA everything. Explosion of frameworks and tools. Total confusion, increased development time.
Now:
No more SPA. Go back to full traditional full page refresh. Hey, still works great!
Now add back a little js… :wink:
(continue…)

10 Likes

Yes! I would love to see this - the simplicity of standard links with the speed of websockets.

I’m not fluent enough in JS and coffeescript to do it very well, but it seems quite do-able, along with an elixir plug to manage the socket and dispatch to the standard phoenix routing.

1 Like

Here’s a site that uses Turbolinks with Phoenix:

As you can see it’s pretty fast: https://changelog.com/

More info: https://changelog.com/posts/changelog-is-open-source

I’ve never heard of Turbolinks before but I like what I see. Definitely can be better than building a slow JS-heavy SPA when it’s not needed.

1 Like

I actually ended up writing something like it since that post, but without a fallback to non-javascript browsers (will not be an issue at my job). It is indeed blazing fast. ^.^

I decided not to return html though, generating it seems useless, I went more of the Discourse method, the server returns just enough html/script to generate a client router, the client router then sets up a websocket connection back to phoenix, hands the url over the websockets, the server sends back an element name to render and a set of dependent files that should be downloaded, the router checks if they are already in the cache, and downloads the ones that are not, once complete it then attaches the element to the page, passing startup data to it (like the websocket connection and more), then swaps the page (via a slide off/in animation, it looks like the pages are attached side-to-side). There is a loading bar at the top just below the header (which is 'static’ish across all pages) that appears when downloading dependencies and waiting for the websocket communication, but thus far I do not even see it blink it is so fast since the site is hosted on the internal company LAN only across a few thousand people, even the VLan remote people do not see any delay. Most of the time it just feels as fast as a native app (and interestingly the html/script of many pages so far is smaller than even the Elm standard library, blehg Elm is huge).

I guess this can be considered an SPA maybe, but only the needed parts are requested from the server (the server can ahead-of-time push messages to the client to have it preload files as well, though I’ve not used it yet).

1 Like

Hello everyone. I’ve added Turbolinks to my Phoenix app. It works great except one scenario:

  1. I have form for creating new record
  2. I fill it with invalid data
  3. I’m redirected back to form and see validation errors.
  4. In my code editor I’m updating form template
  5. My browser updates page because of Phoenix auto reload and raises an error

Any ideas how can I solve this issue?

1 Like

That is just because it tried to GET what was probably only a POST url, that is just because of the auto-reload on a POST I’d imagine?

1 Like

Do anyone have idea how to fix it?

1 Like

A post was split to a new topic: Unpoly - a framework like Turbolinks

I’m using morphdom in my hand-rolled crappy framework.

I basically render Handlebars templates, create a DOM node out of them, then morph the existing DOM. Works quite nicely, but I don’t really care about performance, so no idea how it compares with React’s VDOM.

React’s is very slow. Preact or others like it tend to be much faster. Picodom is a vdom’ish thing that diff’s to the physical dom instead, it is tiny but unsure about speed.

May be a bit off-topic, since somone moved Unpoly to a new thread, but I think it’s worth to mention SPF which YouTube used before the recent redesign. I think Github also was using it, probably not anymore. In this video from 2014 Google describes how it works.

1 Like

Hey, @OvermindDL1, your approach looks interesting.
Is there a blog post/code sample I could consult to learn more?

Any idea what they’re using now instead of it?

No idea. I’ve noticed significant slowdown though.

1 Like

Not as of yet, it’s pretty standard webcomponent stuff, and even then I only use it on the dynamic sections of the site. :slight_smile:

What is the current state of Unpoly and Turbolinks, and their integration with Phoenix now? Do you people recommend either Turbolinks or Unpoly? Is there something better for Phoenix (like some package)?

Using Turbolinks is so easy and it makes a website (somewhat) feel like a native application. Also if you implement progressive web application’s features in a responsive website, it will already feel like a native application on mobiles, and in that case Turolinks is very beneficial, as it saves you the headache of using something like React, Angular, Vue etc and you can create a decent progressive web app using only Turbolinks.

The whole concept of Turbolinks is very beautiful, that with almost no change in the codebase you convert a complete page refresh into something like those applications driven by a JSON API call, an MV* framework/library and Ajax.

1 Like

I use turbolinks in my new app. I havent tested all parts. But for now everything works as rails. I use ex_turbolinks library you can check link.

1 Like

One thing I discovered a few weeks ago (incidentally roughly at the same time as the thechangelog guys) is that any turbolinks redirect plug needs to be placed before fetch_flash/2 of phoenix, as the latter does not retain any flash messages if the http status is not a redirect. But besides that it’s been simple to place turbolinks into phoenix.

I ran into the same problem @MatUrbanski did. Some of my links stopped working.
for some reason after adding turbolink, you need to change the params that you pass to the link helper
I had a problem on the logout link for example
<%= link to: session_path(@conn, :delete), method: :delete, class: "btn btn-default btn-block content-group" do %>
had to be changed to
<%= link to: session_path(@conn, :delete), data: [method: :delete], class: "btn btn-default btn-block content-group" do %>
if you add a data attribute, then the link makes a post request and not a get request anymore.
at the bottom of the readme of turbolinks library is another example

(just ignore the remote: true part if you don’t want to do a remote request)

1 Like