Organize javascript for every Rails route
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.


Auto-magical scaffolding for Paul Irish's DOM-based Routing (or Garber-Irish Implementation) way of organizing your javascript.

Works with turbolinks


Javascript is hard to organize and debugging ajax is a mess. This is one method to organizing your javascript neatly by mirroring the controllers and having all the it outside of your HTML views.

How it works

Setup your namespace

DR.routes = {
	all: {
		html: {
			before: function() {
	demos: {
		html: {
			before: function() {
			demo_action: function() {

What happens

After, requests to demos#demo_action with format html will call the following functions (if they exist):

  • DR.routes.application.html.before
  • DR.routes.demos.html.before
  • DR.routes.application.html.demo_action
  • DR.routes.demos.html.demo_action
  • DR.routes.demos.html.after
  • DR.routes.application.html.after

js format is also supported, i.e.:

  • DR.routes.application.js.before
  • DR.routes.demos.js.before
  • DR.routes.application.js.demo_action
  • DR.routes.demos.js.demo_action
  • DR.routes.demos.js.after
  • DR.routes.application.js.after


Add this line to your application's Gemfile:

gem 'dom_routes'

And then execute:

$ bundle

Add this to your app/assets/javascripts/application.js

//= require dom_routes

Make sure your app/views/layouts/application.html.erb (and all your other layouts) looks like this:

<body data-controller="<%= js_route.controller_path %>" data-action="<%= js_route.action %>">
    <%= execute_js_routes %>

Basic Use

I like to have a JS file for every route in app/assets/javascripts/routes. Like so:


(function() {
	var demos = DR.define('demos', {
		html: {
			edit: function(params) {

		js: {
			new: function(params) {

DR.define() extends or creates the namespace DR.routes.demos and returns it. This allows me to access DR.routes.demos through the demos variable. You can also use the traditional hash namespacing shown in the Setup your namespace section.

So if a html request is sent to demos#edit, DR.routes.demos.html.edit is called with the HTML view rendering.

For a js request sent to demos#new, is called and nothing else happens.

Templates and parameters

Optional Parameters are passed from a JSON DSL (such as jbuilder) and is passed as the params object to the function. You can pass any JSON object as a template.



json.alert_message "ploop"


DR.routes.demos.html.edit({ alert_message: "ploop" });

is called automatically.


For javascript to work, a template must exist. app/views/demos/new.js.jbuilder:

json.log_message "loggggggggggggg"

so{ log_message: "loggggggggggggg" });

is called automatically.

Advanced Use

Manually execute a route

Use #execute_js_route(js_route=self.js_route, format=formats.first)

Executing a different route

Sometimes you want to execute a different route too. For that, you can specify it like so:

self.js_route = "demos/edit" # can be "demos#edit", "edit", { controller: "demos", action: "edit" }, or a DomRoutes::Route object

When this is done, the original route and the new route will be executed.

Handling redirects with flash

Other times you may want to use a route after a redirect, use #flash_js_route(js_route=nil) then.


Extracted out of Placemark. Originally called poke_js.


Feel free to fork, post issues or send any pull requests. Thanks.