The link_to helper in Rails - Rails Tricks Issue 25

30 Jan 2024

The link_to helper in Rails creates an anchor element with the given URL and options. Although the helper has a simple task, it can be used in quite a few ways, and in this article, I will try to cover most of them. Let’s start with creating a simple link:

link_to "Articles", articles_url
# <a href="/articles">Articles</a>

link_to @article.title, article_url(@article)
# <a href="/articles/1">This is my first article</a>

Now if you want, instead of the URL, you can specify URL params as the second parameter to achieve the same result:

link_to @article.title, controller: :articles, :action: :show, id: @article
# <a href="/articles/1">This is my first article</a>

Or to make it compact, you can just pass the object instead of the URL and Rails will infer the URL from the object:

link_to @article.title, @article
# <a href="/articles/1">This is my first article</a>

And since Rails 7, we can compact this even more and just pass an object to link_to. The name will be the result of the to_s method of the object, so in the above example, we override that method on our model:

# app/models/articles.rb
class Article < ApplicationRecord
  def to_s
    title
  end
end

link_to @article
# <a href="/articles/1">This is my first article</a>

There might be a situation when in place of the name, you want to display the whole URL you are linking to. To achieve this, you can pass nil as the first parameter:

link_to nil, "https://external_site.com/"
# <a href="https://external_site.com/">https://external_site.com/</a>

Now imagine that you want to have an icon in front of the name of the link. This helper makes adding that easy. If you call it with the URL and a block, it will use the result of the block for the name attribute:

link_to new_article_path do
  heroicons('plus-circle', class: 'mr-1') + "New article"
end
# <a href="/articles/new"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 mr-1"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" /></svg>New article
</a>

I haven’t mentioned it yet, but the last parameter of the link_to helper is the HTML attributes, so if you want to style your link, you can pass the classes there:

link_to @article.title, @article, class: 'underline'
# <a href="/articles/1" class="underline">This is my first article</a>

Prior to Rails 7, you can pass in the Rails UJS Attributes in this HTML options hash too. So for instance if you want to have a link which does a post request, or uses the delete method, you could do this:

link_to 'Delete article', @article, class: 'underline', method: :delete
# <a href="/articles/1" class="underline" data-method="delete">This is my first article</a>

To ask for a confirmation, you can use the confirm option:

link_to 'Delete article', @article, class: 'underline', method: :delete, confirm: "Are you sure you want to delete '#{@article.title}'?"
# <a href="/articles/1" class="underline" data-method="delete" data-confirm="Are you sure you want to delete 'This is my first article'?">This is my first article</a>

And if you want the link to make an AJAX request, there is the remote option:

link_to 'Delete article', @article, class: 'underline', method: :delete, remote: true
# <a href="/articles/1" class="underline" data-method="delete" data-remote="true">This is my first article</a>

Rails UJS is deprecated since version 7, and Turbo is enabled by default, so instead of the above options, you can use the turbo_method and turbo_confirm options:

link_to 'Delete article', @article, class: 'underline', turbo_method: :delete, turbo_confirm: "Are you sure you want to delete '#{@article.title}'?"
# <a href="/articles/1" class="underline" data-turbo-method="delete" data-turbo-confirm="Are you sure you want to delete 'This is my first article'?">This is my first article</a>

I prefer button_to for any other than GET links because that has a fallback to a regular HTML form in case JavaScript is not enabled or working.

Now consider the situation when you conditionally want to display a link. You can wrap the link_to call into a condition, but Rails has a link_to_if and a link_to_unless helper. I wrote about those helpers in an earlier post: https://greg.molnar.io/blog/rails-tricks-issue-1/

And if we are talking about link_to, there is an important security related information about the helper. The second parameter accepts a string for the href attribute of the a tag. The HTML specification permits various protocols for that attribute, including javascript, so for instance, you can make a dummy link with the following:

link_to "Click me", "javascript: void(0)"

Now let’s say in your application a user can specify the URL for their blog and you pass that to link_to:

link_to "Greg's Blog", @user.blog_url

This user can set the blog URL to javascript: XSS_PAYLOAD, and when someone clicks the link, the browser executes the JavaScript. To mitigate this issue, always validate the format of a URL your application accepts, especially if you intend to use it for linking to that URL.

That’s is for today, until next time!

Hire me for a penetration test

Let's find the security holes before the bad guys do.

Did you enjoy reading this? Sign up to the Rails Tricks newsletter for more content like this!

Or follow me on Twitter

Related posts