Mastering Rails Web Navigation with link_to and button_to Helpers - Part 2

Written on Oct 16, 2023

Welcome to Part 2 (you are reading) of our Rails Web Navigation tutorial!

In Part 1, we embarked on a cosmic journey through the Rails galaxy, exploring the intricacies of the Rails web navigation system. We delved deep into how Rails handles web requests, understanding the celestial dance between ActionController and ActionView. We also navigated the Middleware Nebula, gaining insights into the pivotal role middlewares play in processing web requests.

Having laid the groundwork in Part 1, we're now poised to dive into the heart of web navigation in Rails. In this part, we'll focus on the magic of the link_to and button_to helpers, starting with the ever-versatile link_to. These helpers are the unsung heroes that power the seamless navigation experience we often take for granted. So, without further ado, let's dive right in!

Great, let’s get started!

TABLE OF CONTENTS (Part 2)

link_to

Let me guess, you found your way to this page by clicking on a link from another page, right? That's the beauty of the anchor element, which creates links between pages, files, and locations or external pages.
In the world of Rails, we have amazing helper methods, including link_to, that makes navigating through your Rails application a breeze. This helper generates an HTML <a href=" ">...</a> tag that directs you to a specified URL target. Here's the basic format of a link_to tag and its corresponding HTML link:

<span><%= link_to "AvoHQ", "https://avohq.io/" %></span>
# => <a href="https://avohq.io/">AvoHQ</a>

# Assuming you have a User model and a corresponding user_path helper.
<p><%= link_to user.name, user_path(user) %></p>
# => <a href="/users/123">Adrian M</a>

<li><%= link_to car.type, car_path(@car) %></li>
# even better
<li><%= link_to car.type, @car %></li>
# => <a href="/cars/1">Tesla</a>

As you can see, with link_to, all you need is a string of text AvoHQ for the link's visible name, followed by the path to the destination URL. But that's not all link_to can do! It's able to recognize instances and generate related paths for them. This is useful when you have a list of items (objects) and you want to redirect the user to the show page of the selected item. In the example given, we are redirecting the user to the page of a car, where @car represents a specific instance of a car.

As we discovered earlier, the link_to helper method is pretty useful for creating anchor tags that link to specified URL targets. But did you know that the method's full syntax is even more powerful? That's right! The complete method signature includes additional parameters that allow you to customize the link even further. Here's the full signature of the link_to method:
**link_to**(name = nil, options = nil, html_options = nil, &block)

Let's break it down.

The link_to method accepts several parameters, with some being mandatory and others optional.

  • name: This parameter by default is nil. It is the text that appears as the link's visible text. It is what you see on the page. If name is nil, the URL is used as the link visible text. name can be string, image tag, or a combination of both as we will see soon.
<li><%= link_to 'Home', root_path %></li>
# => <a href="https://avohq.io/">Home/</a>

<span><%= link_to "https://avohq.io/" %></span>
# => <a href="https://avohq.io/">https://avohq.io/</a>
  • options: is another parameter that link_to accepts. By default, it is nil. It is responsible for defining the URL or route for the link. If it is a string, it is represented as the URL of the link. If it is a hash, it follows the defined route. There are two styles of routes: the older-style parameters routes, which no one uses anymore, and the new-style named routes.

The older-style parameters routes use the keys controller, action, and id to specify the controller, action, and ID of the resource being linked to, respectively. It is not recommended to use this style of routing anymore.

link_to "User", controller: "users", action: "show", id: @user
# => <a href="/users/show/1">User</a>
  • And there's the current Rails style of RESTful routes, which is considered better practice.
link_to "User", @user
# => <a href="/users/1">User</a>
  • html_options is another parameter that the link_to accepts. It is nil by default, and it is used to define HTML attributes that will be added to the link's a tag. You can use this parameter to add class, id, target, rel, type, and data attributes to the link, among others. By doing this, you can customize the link's behaviour and appearance.
link_to 'AvoHQ', 'https://avohq.io', class: 'to_home', target: '_blank'
# => <a href="https://avohq.io" class="to_home" target="_blank">AvoHQ</a>

# Adding a class and id to the link
link_to "AvoHQ", "https://avohq.io/", class: "btn btn-primary", id: "avohq-link"
# => <a href="https://avohq.io/" class="btn btn-primary" id="avohq-link">AvoHQ</a>

Here we create a link to AvoHQ's homepage with the visible text AvoHQ. Additionally, it includes CSS class and target attributes that are added to the a tag of the link.

link_to "Avo Blog", posts_path, id: "blog", class: "avo_blog"
# => <a href="/posts" class="avo_blog" id="blog">Avo Blog</a>

This code generates a link to AvoHQ's blog page with the text Avo Blog and a CSS and id attributes added to the link's a tag.

link_to "Comment section", user_path(@user, anchor: "section")
# => <a href="/users/1#section">Comment section</a>

This code generates a link to the comment section on the same user show page with the text Comment section and an anchor attribute added to the link to target that specific section.

link_to "Avo Home", "http://www.avohq.io/", target: "_blank", rel: "nofollow"
# => <a href="http://www.avohq.io/" target="_blank" rel="nofollow">Avo Home</a>

This code generates a link to the Avo home page with the text Avo Home, and it also adds the target and rel attributes to the link's a tag.

link_to "Avo search", controller: "searches", query: "ruby on rails admin panel"
# => <a href="/searches?query=ruby+on+rails+admin+panel">Avo search</a>

This code generates a link to the search page with the text Avo search. In addition, a controller with the name 'searches' and a query attribute with the value 'ruby on rails admin panel' are added to the link's a tag.

  • &block parameter is optional and is nil by default. It's not used often but can be useful for making the code more readable and easier to understand. Imagine you have a long text that is hard to fit into the name parameter. In this case, you can pass the link text inside a block of code that generates the content of the link.
<%= link_to(@car) do %>
  <strong><%= @car.name %></strong> -- <span>Check this car!</span>
<% end %>
<a href="/cars/1">
  <strong>Tesla</strong> -- <span>Check this car!</span>
</a>


<%= link_to "https://avohq.io/" do %>
  <strong>AvoHQ</strong> - The Best Rails Guide
<% end %>
<a href="https://avohq.io/">
  <strong>AvoHQ</strong> - The Best Rails Guide
</a>

Another example:

<%= link_to blog_path(@blog) do %>
  <i class="fa fa-pencil"></i> # fontawesome icons
  Edit
<% end %>

# =>
<a href="/blogs/1">
  <i class="fa fa-pencil"></i>
  Edit
</a>

As you can see, this code generates a link to the show action of the BlogController, passing the @blog ID as a parameter. The link's content will consist of a Font Awesome icon and the text Edit.

So far, we learned how link_to works in its default mode, which is the GET request. To refresh your memory, do you remember our fancy diagram from earlier? It illustrated the communication between the browser and the server, where a method was used to deliver the message of what the browser wants the server to do. If you don't recall, don't worry, it's normal to forget things. In this case, the localhost/jobs URL is clicked by a user, and a successful request with a 200/OK response takes the user to the Jobs page, which uses the GET request method.

link_to syntax
link_to syntax get response

Did you know that there's another way link_to can navigate? Yes, it's true! This method uses a DELETE request to delete a record or a specific instance. Let's say you have a @post instance and you want to delete it. In order to call the destroy action on the PostController, you need to use the DELETE request method. You can achieve this by passing the method: :delete hash as an option to the link_to helper. For example:
%%On the same token, you will be able to pass any method defined in your controllers to the link_to helper. Here is how to use delete method:%%

<%= link_to "Delete post", post_path(@post), method: "delete" %>
# => <a rel="nofollow" data-method="delete" href="/posts/1">Remove</a>

It is worth noting that the rel="nofollow" attribute is automatically added by Rails 'magic' as an added benefit for Search Engine Optimization (SEO).

Deleting a record is a necessary action, but it can be harsh and irreversible. To ensure that the user agrees to such a drastic action, we developers might want to prompt them with a message asking for confirmation and to prevent accidental deletion using confirm method { confirm: "Are you sure?" }. The easiest way to add this feature is with a simple JavaScript alert box that will ask the user to confirm their delete request. Guess what, Rails magic does it for you, again. The alert can be implemented as shown below:

<%= link_to "Delete post", post_path(@post), method: "delete", { confirm: "Are you sure?" } %>
# => <a data-confirm="Are you sure?" rel="nofollow" data-method="delete" href="/jobs/1">Delete Post</a>

link_to with confirmation window
link_to with confirmation window
link_to with confirmation window

Ajax links, the superheroes of asynchronous requests that allow you to dynamically update your page content without the irritating full-page reloads. To create an Ajax link, all you need to do is use the link_to method with the remote: true. For instance, the following code creates an Ajax link that sends a GET request to the server when clicked:

<%= link_to "Load More", "https://avohq.com/more", remote: true %>

When clicked, this link triggers an Ajax request to the specified URL, allowing your web page to update without any hiccups. With the power of JavaScript or other client-side technologies such as Hotwire as we will discover soon, you can even process and display the server's response on your page.

Variants

Rails not only empowers you with the link_to magic implementation, but it has more variations up its sleeve! Oh, yeah! Each of these variants helps you avoid complex code and makes your code clean by doing a specific communication job. These variants are:

  1. link_to_unless_current(name, options = {}, html_options = {}, &block) Want to create a link only if it's not the current page? Try link_to_unless_current! It creates a link but returns the name instead if it's the current page.
  <ul>
    <li><%= link_to_unless_current "Cars", cars_path %></li>
    <li><%= link_to_unless_current "Dashboard", dashboard_path %></li>
  </ul>

  # When you are on `/cars`, this template will output:
  # =>
  <ul>
    <li>Cars</li>
    <li><a href="/dashboard">Dashboard</a></li>
  </nav>
  1. link_to_if(condition, name, options = {}, html_options = {}, &block) Want to create a link only under certain conditions? Use link_to_if! It creates a link using options if a condition is true, otherwise it returns the name.
  <li>
    <%= link_to_unless(@current_user.nil?, 'Log In', new_user_registration_path %>
  </li>

  # If the user is NOT logged in...
  # =>
  <li><a href="/users/signin/">Log In</a></li>
  1. link_to_unless(condition, name, options = {}, html_options = {}, &block) Looking for a way to create a link only when a certain condition is false? Try link_to_unless! It creates a link tag using a URL from options unless the condition is true, otherwise, only the name is returned.
  <li>
    <%= link_to_unless(@current_user.nil?, 'Log In', new_user_registration_path %>
  </li>

  # If the user is logged in...
  # =>

  <li><a href="/users/signin/">Log In</a></li>

If you think you have seen enough Rails magic, you are mistaken my friend. Rails have a new trick up its sleeve: Hotwire. And with the magical Turbo tool that comes with it, you can create modern, interactive web applications with minimal, or sometimes no JavaScript at all, providing users with an incredibly smooth experience.

One of Turbo's key instruments is Drive, which enhances page-level navigation. It watches for link clicks and form submissions, performs them in the background, and updates the page without doing a full reload. And let me tell you, that's pretty darn amazing.

link_to default uses Turbo Drive for non-GET (POST, DELETE) requests, making it simple to create links that update the page content without a full reload. Perfect for actions like deleting, creating/updating records and displaying more content.

For example, let's say you want to create a link that uses Hotwire to submit a DELETE request to the server, with a confirmation dialog for the user to approve. Well, look no further:

  <%= link_to
    "Delete Post",
    post_path(post),
    method: :delete,
    data: { turbo_confirm: "Are you sure?" }
  %>

When clicked, this link sends a DELETE request to the server and displays a confirmation dialog for the user, (we have seen this before, nothing new here, right)! Turbo then processes the server response, and the page content is updated without a full page reload (That's magic). This feature makes it easy to create interactive web applications with minimal or no JavaScript and a smooth user experience. Why not give it a try and see the magic for yourself?

Phew, it has been a journey. We've navigated our way through the astonishing Rails web navigation system. link_to is one of the powerful tools in Rails world. Effortlessly, you can generate HTML tags that glide you to pages, files, and beyond. Its signature is versatile that you can add blocks, instance variables, media, confirmation messages, and even delete objects. Additionally, it offers seamless variants, making link creation clutter-free, logical and magical.

button_to

button_to Definition

Similar to link_to, button_to is another handy helper method in Rails that creates a button element with a specified action. This helper generates a nifty HTML form <form action=" "><button>...</button></form> tag that performs the specified action submit when clicked. Here's the basic format of a button_to tag and its corresponding HTML form:

  <%= button_to "Delete", post_path(@post), method: :delete %>
  # =>
  <form action="/posts/1" method="post">
    <input type="hidden" name="_method" value="delete" />
    <input type="submit" value="Delete" />
  </form>

As you can see, with button_to, you need to specify the action (in this case, Delete), followed by the path to the destination URL (post_path(@post)), and the method used for the action (method: :delete). When clicked, this button sends a DELETE request to the specified URL. button_to. And that's not all! button_to accepts other options such as :class, :disabled, and :form_class to customize the form element and its children.

It's particularly useful for actions that require more than a simple link, such as creating or deleting records in a database. By default, button_to generates a form element with a hidden input for the method and a submit button with the specified label, making it easy to perform complex actions without having to write a lot of HTML and JavaScript code.

button_to Syntax

Ah, button_to - another gem in the Rails toolbox! As we discovered earlier, button_to is a helper method that generates a form element with a specified action, which is executed when the button is clicked. By now you would know that the method's full syntax is even more powerful. The complete method signature includes additional parameters that allow you to customize button_to even further. Here's the full signature of the button_to method:
**button_to**(name = nil, options = nil, html_options = nil, &block)

Let's break it down.

  • name: This parameter is optional and represents the text that appears on the button. If no name is provided, the button_to method will use the URL as the button text. You can use a string, an image tag, or a combination of both as the name argument.
  <li><%= button_to "Delete", post_path(@post), method: :delete %></li>
  # =>
  <form action="/posts/1" method="post">
    <input type="hidden" name="_method" value="delete" />
    <input type="submit" value="Delete" />
  </form>
  • options: This parameter is optional and can take several forms. If it is a string, it is interpreted as the URL of the button's destination. If it is a hash, it can include a :controller, :action, and :id keys to specify the controller, action, and ID of the resource being linked to, respectively (Similar to link_to). Other options include :method to specify the HTTP method used for the action (e.g., :post, :put, :delete), and :data to specify additional data to be sent with the request.

The options parameter of button_to can take a hash with additional options, like :params, which specifies any additional parameters to be sent with the request. Here's an example:

  button_to "New Article", new_article_path, params: { time: Time.now }
  # =>
  <form action="/articles/new" method="post">
    <input type="hidden" name="authenticity_token" value="[AUTH_TOKEN]" />
    <input type="hidden" name="time" value="[CURRENT_TIME]" />
    <button type="submit">New Article</button>
  </form>

Here, a form element is generated with an action of /articles/new and a method of post. The params option is used to add a hidden input field named time with the value of the current time. When the button is clicked, the form is submitted and the specified action is performed on the server.

  • html_options is an optional parameter of the button_to, Is nil by default and used to add HTML attributes to the button's form and button tags. Here are some examples of using the various html_options:
  1. :method: This option specifies the HTTP verb (delete) to be used for the form submission. Here's an example:

    button_to "Delete", post_path(@post), method: :delete
    # =>
    <form action="/posts/:id" method="post">
    <input type="hidden" name="_method" value="delete" />
    <input type="submit" name="commit" value="Delete" data-disable-with="Delete" />
    <input type="hidden" name="authenticity_token" value="[AUTH_TOKEN]" />
    </form>
    
  2. :disabled: This option generates a disabled button. Here's an example:

    button_to "Save", save_path, disabled: true
    # =>
    <form action="/save" method="post">
    <input type="hidden" name="authenticity_token" value="[AUTH_TOKEN]" />
    <button type="submit" disabled="disabled">Save</button>
    </form>
    
  3. :data: This option is used to add custom data attributes. Here's an example:

    button_to "Submit", submit_path, data: { confirm: "Are you sure you want to submit?" }
    # =>
    <form class="button_to" method="post" action="/submit">
    <input type="hidden" name="_method" value="post">
    <input type="hidden" name="authenticity_token" value="[AUTH_TOKEN]">
    <button data-confirm="Are you sure you want to submit?" type="submit">Submit</button>
    </form>
    
  4. :remote: This option allows the unnoticeable JavaScript drivers to control the form's submission behaviour. By default, this is an AJAX submit. Here's an example:

    button_to "Delete", post_path(@post), method: :delete, remote: true
    # =>
    <form class="button_to" method="post" action="/posts/1" data-remote="true">
    <input type="hidden" name="_method" value="delete" />
    <input type="hidden" name="authenticity_token" value="[AUTH_TOKEN]" />
    <button>Delete</button>
    </form>
    
  5. :form: This option is a hash of attributes to be added to the form tag. Here's an example:

    button_to "Create", create_path, form: { id: "create-form", class: "create-form" }
    # =>
    <form accept-charset="UTF-8" action="/create" class="create-form" id="create-form" method="post">
    <input type="hidden" name="authenticity_token" value="[AUTH_TOKEN]" />
    <button name="button" type="submit">Create</button>
    </form>
    
  6. :form_class: This option is used to specify the class of the form within which the submit button will be placed. Here's an example:

    button_to "Save", save_path, form_class: "save-form"
    # =>
    <form action="/save" class="save-form" method="post">
    <input type="hidden" name="authenticity_token" value="[AUTH_TOKEN]" />
    <button type="submit"> Save </button>
    </form>
    
  7. :params: This option is a hash of parameters to be rendered as hidden fields within the form. Here's an example:

  button_to "New Car", new_car_path, params: { time: Time.now }
  # =>
  <form action="/cars/new" method="post">
    <input type="hidden" name="time" value="[CURRENT_TIME]" />
    <button type="submit">New Car</button>
  </form>
  1. &block is an optional parameter of the button_to. It is nil by default and can be used to generate custom content for the button. This is useful when you want to add an icon or other custom HTML to the button.
<%= button_to post_path(@post) do %>
    <i class="fa fa-trash"></i>  # fontawesome icons
    Delete
<% end %>

# =>
<form action="/posts/1" method="post">
  <input type="hidden" name="_method" value="delete" />
  <button type="submit">
    <i class="fa fa-trash"></i>
    Delete
  </button>
</form>

This code will generate a delete button with an icon and the text Delete. The block is used to generate the content of the button, which is then wrapped in a form that sends a DELETE request to the specified path when clicked. Get creative with your buttons and make them stand out!

<%= button_to user_path(@user) do %>
    <%= image_tag("delete.png") %>
<% end %>
# =>
<form action="/users/1" method="post">
    <input type="hidden" name="_method" value="delete">
    <button type="submit">
        <img src="/assets/delete.png">
    </button>
</form>

This will create a button with an image of a trash can that, when clicked, sends a DELETE request to the path specified by user_path(@user).

Submit with options

Another example of button_to wtih few options and html_options.

So, what we have here is a button_to method that generates a form with a button inside. This button is used to generate a short summary of a company's description using AI.

button_to submit with few options

<%= button_to "Generate Description Summary",
    description_summary_company_path(@company),
    method: :patch,
    class:'btn bg-indigo-50 disabled:opacity-50',
    data: { disable_with: "A short description summary in the making..."}
%>
# =>
<form class="button_to"
    method="post"
    action="/startups/gamucatex/description_summary">
    <input type="hidden" name="_method" value="patch" autocomplete="off">
    <button class="btn bg-indigo-50 disabled:opacity-50"
        data-disable-with="A short description summary in the making..."
        type="submit">
        Generate Description Summary
    </button>
    <input type="hidden"
        name="authenticity_token"
        value="yTWxr5_58rb_LqRQl36wgQpFh9K7a....."
        autocomplete="off">
</form>

The generated HTML code includes a form tag with a method of `post` and an action attribute set to the description_summary_company_path route. The method is overridden by the method option, which sets it to `patch` because most browsers don't support methods other than `GET` and `POST` when it comes to submitting forms. Amazingly, Rails works around this issue by matching other methods over POST with a hidden input named "_method", which is set to reflect the desired method patch.

The HTML options include a class attribute, which sets the CSS class for the button to `btn bg-indigo-50 disabled:opacity-50`. This will give the button a nice background colour and make it slightly transparent when disabled. I'm using TailwindCSS for styling.

There is a data attribute with a disable_with key that sets the text to display when the button is clicked, which is A short description summary in the making.... This helps to provide feedback to the user that the button is doing something.

Finally, the generated HTML code includes a hidden input tag with a CSRF token authenticity_token is included to protect against cross-site request forgery (CSRF) attacks.

Hotwire with button_to

Similar to link_to, Hotwire and it's tools, can enhanced button_to to create modern, interactive web applications. By default, button_to generates a form with a button that sends a POST request to the server when clicked. However, with Hotwire and the Turbo tool, button_to can be used to create smooth, fast user experiences without a full page reload.

For example, let's say you want to create a button that sends a DELETE request to the server, with a confirmation dialog for the user to approve. With Hotwire, you can use the data-turbo-confirm attribute to display a confirmation dialog to the user, just like with link_to. Here's an example:

<%= button_to
    "Delete Post",
    post_path(post),
    method: :delete,
    data: { turbo_confirm: "Are you sure?" }
%>

When you click the button created with button_to, it will submit a DELETE request to the server and show a confirmation dialog, similar to link_to. Then Turbo comes in, processes the server response, and voila! The page content is updated without any page refresh with little or no JavaScript.

button_to Conclusion

Well, well, well. We've been exploring the magnificent world of Rails navigation tools, and button_to is yet another powerful one.
This incredible helper method allows us with minimal effort, you can create buttons that can perform a variety of actions such as submitting forms, sending requests to the server, and updating page content. button_to also supports various options such as method, params, class, and data, allowing for even more flexibility and customization. And with the integration of Hotwire, button_to can provide users with a smooth and fast experience without the need for excessive JavaScript. Overall, button_to is a valuable addition to any Rails developer's toolkit.

Final Words

After all is said and done, link_to and button_to are both amazing helpers in the Rails world. With the help of Hotwire and Turbo, they offer fast and smooth navigation without needing to write tons of JavaScript. Whether you need to generate simple links or complex buttons, these helpers have got you covered. So, go ahead and use them to create your own magical and interactive web applications with ease!

Frequently Asked Questions (FAQs)

  1. What is link_to and button_to in Rails?

    link_to and button_to are helper methods in Ruby on Rails that generate HTML links and buttons, respectively. They are commonly used in views to create hyperlinks and form submissions that send requests to the server.

  2. What is the difference between link_to and button_to?

    The main difference between link_to and button_to is that link_to generates a hyperlink that navigates the user to a new page, while button_to creates a form that submits a request to the server without navigating to a new page. However, both methods can be configured to use Ajax requests with Hotwire Turbo Drive in Rails 7, which is amazing!

  3. How do I use link_to and button_to in Rails?

    Both methods accept three required parameters: the link text or button label, the URL to which the request should be sent, and the HTTP method to use (GET, POST, PATCH, PUT, DELETE). link_to also accepts optional parameters for specifying attributes such as class, id, and data. button_to accepts similar optional parameters, such as method, remote, form_class, and params.

  4. How do I add a confirmation message to link_to and button_to?

    You can add a confirmation message to link_to and button_to by including the data: { confirm: "Are you sure?" } option. When the link or button is clicked, a confirmation dialog box will be displayed with the specified message.

  5. How do I add custom HTML to link_to and button_to?
    You can use a block to add custom HTML to link_to and button_to. Simply wrap the link text or button label in a block and include any additional HTML or Ruby code inside the block. For example:

      <%= link_to post_path(@post) do %>
        <i class="fa fa-pencil"></i>
        Edit Post
      <% end %>
    
  6. Can I use link_to and button_to with Rails Hotwire?

    Yes, you can use link_to and button_to with Rails Hotwire by adding the data-turbo="false" attribute to the link or button. This will disable Turbo Drive for that link or button, and the request will be processed with a full page reload. Alternatively, you can configure link_to and button_to to use Turbo Drive for non-GET requests by default in Rails 7 and later versions.

Subscribe to Design and Develpment ideas

Enter your email to subscribe to a once-monthly newsletter curating the latest content on Rails, Hotwire, and other things you might find interesting.

Get in touch