Last Updated: September 27, 2021
·
13.02K
· michiels

Sample to get started with PJAX in Rails

So I have been working on trying out PJAX. PJAX is a technique where you can update a certain part of a page WITH support to change the browser URL bar when the request finishes.

So basically, it's AJAX + pushState.

This way you can turn a link like http://mysite.com/#!/books/5 into http://mysite.com/books/5 while still remaining a single-page design.

One big advantage of this is that the new request will not have to reload any common elements (like your header and your footer) and it will also not have to reload your stylesheets, javascripts which makes it super fast to load.

You can easily get started by using the jQuery PJAX plugin if you are using jQuery: https://github.com/defunkt/jquery-pjax

Add the code to your Rails project and you are good to go. Here is an example of a Dutch website I am working on: http://volgendboek.nl.

In my application.js I make sure all my <a> tags will be loaded using PJAX:

application.js

$('a').pjax('#main')

This single line causes every link to be loaded with PJAX. It will first load the page and replaces the element with id="main" with the content loaded from the PJAX request.

Then, it will update the browser URL bar to reflect the new URL.

Here is a shortened snippet of my application.html.erb layout:

application.html.erb

<html>
<head>
  <title>Boek uit? Vind je volgende fantasy boek</title>
  <%= stylesheet_link_tag    "application", :media => "all" %>
  <%= javascript_include_tag "application" %>
  <%= csrf_meta_tags %>
</head>
<body>
  <div class="header">
    <div class="logo">
      <%= link_to root_path do %>
        volgendboek<span>.nl</span>
      <% end %>
    </div>
  </div>

  <div class="wrapper">
    <div id="main">
      <%= yield %>
    </div>
  </div>
</body>
</html>

All new pages will just render everything with the layout and put the view inside the <div id="main"></div> tag. When loading the page with PJAX it will put the returned html into this div.

This is the html a view renders:

app/views/series/show.html.erb

<title>Serie: <%= @series.title %></title>
<div class="container">
  <h1><%= @series.title %></h1>
  <div class="books">
    <%= render @series.books %>
  </div>
</div>

When making your view, you need to think of two things:

First, make sure to include a <title> tag in your view. Since jQuery PJAX will also update your browser's title bar. Which is awesome!

Second, it's important to wrap all the content you would like to load in some kind of container element. I use <div class="container"> here but you can use any element as container. This is important because jQuery PJAX will look for a container element in the response to load into the page.

There's also a thing you need to do in your controller. You need to make sure your layout does not get rendered with a PJAX request.

So, for instance in my BooksController I have:

class BooksController < ApplicationController
  def show
    @book = Book.find(params[:id])
    @series = @book.series
    @offset = params[:offset]

    if request.xhr?
      render :layout => false
    end
  end
end

I am using request.xhr? to check if it's a PJAX request since every PJAX request is an AJAX request.

You can use the https://github.com/rails/pjax_rails plugin, which adds a helper to do these kind of things so you can write:

if pjax_request?
  render :layout => false
end

Take a look at that plugin. It has some nice features for using PJAX in Rails.

6 Responses
Add your response

Check this out as well! https://github.com/rails/turbolinks

over 1 year ago ·

@tnypxl Turbolinks is awesome! Especially when you're not using a JS framework.

over 1 year ago ·

What happens to mailto links, external links? Also, wouldn't it be better to tailor each link to only reload what's necessary instead of everything but the header and footer (although I guess you get most of the benefits by avoiding to reload the CSS and JavaScript everytime)?

over 1 year ago ·

@mbillard jQuery PJAX (and all newer Rails gems and implementations) degrade correctly. If it doesn't get a response within x ms or if it doesn't get a compatible response at all, it will just point the browser to the original URL just like clicking a normal link.

I think the benefit is mostly not having to reload the entire page and re-interprate all the JS en CSS (and not hitting your server for it). But, I've just PJAX for just loading certain parts of my page as well. For instance, reloading a "content" div and not the navigation and footer.

over 1 year ago ·

@mbillard: you can use any selector in the jQuery pjax call, I prefer to use something like $('a.pjax').pjax('#pjax-container'), which gives a bit more control over what to pjax and what not. but as PJAX degrades very well, I don't think mailto or external links would suffer in the least anyway.

over 1 year ago ·

What is the expression:

<a href="/book">@book</a> = Book.find(params[:id])
@series = <a href="/book">@book</a>.series

??
<a href="/book">,.... is nothing in Ruby.
Please correct for other to read and use.
Thank you, sir.

over 1 year ago ·