Link
A link component that renders <a> elements with conditional rendering support and full Turbo integration.
Key Features
- Conditional Rendering, Support for
if:,unless:, andunless_current: - Turbo Integration, Full support for Turbo Frame, Stream, Method, and Prefetch
- External Links, Automatic security attributes for external URLs
- Icon Support, Leading and trailing icons
- Underline Variants, Four different underline styles
- Active State, Highlight navigation/tabs with active styling
- Text Truncation, Auto-truncate long text with tooltips
- SEO Features, Enhanced rel attributes (sponsored, ugc, nofollow)
- Communication Links, Shortcuts for email, phone, and SMS links
- Accessibility, HrefLang, tooltips, and referrer policy support
Basic Usage
The link component supports both positional and keyword argument styles. By default, links are blue and show an underline on hover.
<%= rui_link("/posts", "View Posts") %>
<%= rui_link("/posts", "View Posts", color: :primary) %>
<%= rui_link("/posts", color: :primary) do %>
View Posts
<% end %>
<%= rui_link(url: "/posts", text: "View Posts", color: :primary) %>
Variants
Four distinct underline styles. The default is hover_underline, which shows an underline on hover for clear visual feedback.
<%= rui_link("#", "Underline", variant: :underline) %>
<%= rui_link("#", "No Underline", variant: :no_underline) %>
<%= rui_link("#", "Hover Underline") %>
<%= rui_link("#", "Animated Underline", variant: :animated_underline) %>
Navigation Variant (:nav)
The :nav variant is designed for sidebar and navigation menus. It features:
- Auto-detect active state - Automatically highlights the current page
- Block-level styling - Full-width with padding and rounded corners
- Hover backgrounds - Subtle hover effect for better UX
- Color theming - Supports all colors for themed sections
💡 Pro Tip: The :nav variant defaults to color: :neutral for standard navigation.
The active state is automatically detected using Rails' current_page? helper.
<nav class="space-y-1">
<%= rui_link("/docs/link", "Link", variant: :nav) %>
<%= rui_link("/docs/button", "Button", variant: :nav) %>
<%= rui_link("/docs/icon", "Icon", variant: :nav) %>
</nav>
Colored Navigation Sections
Use colors to create themed navigation sections:
Default (Neutral)
Blue Section
Green Section
<nav class="space-y-1">
<%= rui_link("/analytics", "Analytics", variant: :nav, color: :blue) %>
<%= rui_link("/reports", "Reports", variant: :nav, color: :blue) %>
</nav>
<%= rui_link("/billing", "Billing", variant: :nav, color: :green, active: true) %>
Navigation with Icons
Combine :nav variant with icons for rich sidebar navigation:
<nav class="space-y-1">
<%= rui_link("/dashboard", "Dashboard", variant: :nav) do |link| %>
<% link.with_icon(:layout_dashboard) %>
<% end %>
<%= rui_link("/projects", "Projects", variant: :nav) do |link| %>
<% link.with_icon(:folder) %>
<% end %>
<%= rui_link("/team", "Team", variant: :nav) do |link| %>
<% link.with_icon(:users) %>
<% end %>
</nav>
Colors
All semantic and Tailwind colors are supported via ColorBuilderHelper. The default is blue.
Semantic Colors
Configurable semantic colors that map to Tailwind colors:
Tailwind Colors
All Tailwind default colors are supported:
<%= rui_link("#", "Primary", color: :primary) %>
<%= rui_link("#", "Success", color: :success) %>
<%= rui_link("#", "Danger", color: :danger) %>
<%= rui_link("#", "Purple", color: :purple) %>
<%= rui_link("#", "Teal", color: :teal) %>
<%= rui_link("#", "Rose", color: :rose) %>
Sizes
Five size options from xs to xl.
<%= rui_link("#", "Small Link", size: :sm) %>
<%= rui_link("#", "Base Link", size: :base) %>
<%= rui_link("#", "Large Link", size: :lg) %>
Conditional Rendering
Links can conditionally render as text based on conditions.
<%= rui_link("/admin", "Admin Panel", if: admin?, color: :primary) %>
<%= rui_link("/login", "Login", unless: user_signed_in?) %>
<%= rui_link(posts_path, "Posts", unless_current: true) %>
States
Disabled and loading states are supported.
<%= rui_link("#", "Disabled", disabled: true) %>
<%= rui_link("#", "Loading", loading: true) %>
External Links & Security
External links automatically get security attributes to protect your users and prevent common vulnerabilities.
🔒 Security by Default
Per MDN recommendations , external links automatically include:
rel="noopener"- Preventswindow.openerexploitation (tabnabbing attacks)rel="noreferrer"- Blocks referrer information from being senttarget="_blank"- Opens in new tab for better UX
You don't need to do anything - security is automatic when using external: true or target: "_blank".
💡 The link above uses external: true - no need to specify rel or target, it's automatic!
<%= rui_link("https://github.com", "Visit GitHub", external: true) %>
<%= rui_link("https://example.com", "Example", target: "_blank") %>
With Icons
Links support leading and trailing icons.
<% rui_link("#", "Download", color: :primary) do |link| %>
<% link.with_icon(:download) %>
<% end %>
<% rui_link("#", "Next", color: :primary) do |link| %>
<% link.with_icon(:arrow_right, position: :trailing) %>
<% end %>
<% rui_link("#", "Settings", color: :primary) do |link| %>
<% link.with_icon(:settings) %>
<% end %>
Turbo Integration
Full support for Hotwire Turbo features: Frames, Streams, Methods, Prefetch, and more.
⚡ Turbo-Powered: Build modern, SPA-like experiences without JavaScript. All Turbo features work seamlessly with rui_link.
Basic Turbo Features
<%= rui_link("/posts/1/edit", "Edit", turbo_frame: "post_modal") %>
<%= rui_link("/posts/1", "Delete",
turbo_method: :delete,
turbo_confirm: "Are you sure?",
color: :danger) %>
<%= rui_link("/posts", "Load More", turbo_stream: true) %>
<%= rui_link("/posts", "Posts", turbo_prefetch: true) %>
<%= rui_link("/posts", "Refresh", turbo_action: :replace) %>
Real-World Example: Blog Post Management
Complete example showing Turbo Frames, Methods, Streams, and Prefetch working together.
<turbo-frame id="posts-list">
<% @posts.each do |post| %>
<div class="post-card border rounded-lg p-4 mb-4">
<h3 class="text-lg font-bold"><%= post.title %></h3>
<p class="text-zinc-600 mb-3"><%= post.excerpt %></p>
<div class="flex gap-2">
<%= rui_link(edit_post_path(post), "Edit",
turbo_frame: "modal",
color: :primary) do |link|
link.with_icon(:edit)
end %>
<% if post.published? %>
<%= rui_link(unpublish_post_path(post), "Unpublish",
turbo_method: :patch,
turbo_confirm: "Unpublish this post?",
color: :warning) do |link|
link.with_icon(:eye_off)
end %>
<% else %>
<%= rui_link(publish_post_path(post), "Publish",
turbo_method: :patch,
color: :success) do |link|
link.with_icon(:send)
end %>
<% end %>
<%= rui_link(post_path(post), "Delete",
turbo_method: :delete,
turbo_confirm: "Delete '#{post.title}'? This cannot be undone.",
color: :danger) do |link|
link.with_icon(:trash_2)
end %>
<%= rui_link(post_path(post), "View",
turbo_prefetch: true,
variant: :outline,
color: :blue) do |link|
link.with_icon(:eye)
end %>
</div>
</div>
<% end %>
<% if @next_page %>
<%= rui_link(posts_path(page: @next_page), "Load More Posts",
turbo_stream: true,
variant: :outline,
full_width: true) do |link|
link.with_icon(:chevron_down, position: :trailing)
end %>
<% end %>
</turbo-frame>
<turbo-frame id="modal" class="fixed inset-0 bg-black/50 hidden">
</turbo-frame>
Controller Actions
class PostsController < ApplicationController
def publish
@post = Post.find(params[:id])
@post.update(published: true)
redirect_to posts_path, notice: "Post published!"
end
def unpublish
@post = Post.find(params[:id])
@post.update(published: false)
redirect_to posts_path, notice: "Post unpublished!"
end
def destroy
@post = Post.find(params[:id])
@post.destroy
redirect_to posts_path, notice: "Post deleted!"
end
def index
@posts = Post.page(params[:page])
@next_page = @posts.next_page
respond_to do |format|
format.html
format.turbo_stream
end
end
end
Navigation with Instant Loading
Use turbo_prefetch: true for instant navigation between pages.
<nav class="flex gap-6">
<%= rui_link("/", "Home",
turbo_prefetch: true,
unless_current: true) %>
<%= rui_link("/posts", "Blog",
turbo_prefetch: true,
unless_current: true) %>
<%= rui_link("/about", "About",
turbo_prefetch: true,
unless_current: true) %>
</nav>
<div class="post-navigation flex justify-between mt-8">
<% if @post.previous %>
<%= rui_link(post_path(@post.previous), turbo_prefetch: true) do |link|
link.with_icon(:arrow_left)
"← #{@post.previous.title}"
end %>
<% end %>
<%= rui_link(posts_path, "All Posts",
turbo_prefetch: true,
turbo_action: :replace) %>
<% if @post.next %>
<%= rui_link(post_path(@post.next), turbo_prefetch: true) do |link|
"#{@post.next.title} →"
link.with_icon(:arrow_right, position: :trailing)
end %>
<% end %>
</div>
🎯 Live Interactive Examples
See rui_link with Turbo in action! Visit the Post pages to interact with real working examples.
✨ What to Try on Post Pages
- 🚀 Hover over "View" links - watch the network tab as pages prefetch instantly!
- ⚡ Click "Edit" links - they load instantly (thanks to prefetch)
- 🔥 Try "Publish" link - uses
turbo_method: :patchwithout page reload - ❌ Try "Delete" link - see Turbo confirmation dialogs in action
- 🔄 Navigate between posts - experience SPA-like speed!
Quick Prefetch Demo
Hover over these links and watch your browser's network tab - they prefetch on hover!
💡 Pro Tip: Open DevTools Network tab, then hover (don't click) - you'll see the page fetching!
💡 Turbo Best Practices
- ✅ Use
turbo_prefetchfor navigation links (instant page loads) - ✅ Use
turbo_methodfor quick actions (publish, delete, archive) - ✅ Use
turbo_framefor modals and inline editing - ✅ Use
turbo_streamfor paginated content (load more, infinite scroll) - ✅ Use
turbo_confirmfor destructive actions (delete, unpublish) - ⚠️ Don't use
turbo_prefetchon forms or actions that change data
Advanced Features
Additional features for enhanced UX, SEO, and accessibility.
Active State
Highlight the current/active link in navigation or tabs.
<%= rui_link("#", "Active", active: true) %>
<%= rui_link("/posts", "Posts", active: current_page?("/posts")) %>
Tooltips
Add tooltip text that appears on hover.
<%= rui_link("/docs", "API", title: "View API Documentation") %>
<%= rui_link("/docs", "API", tooltip: "View API Documentation") %>
Text Truncation
Automatically truncate long text with tooltip showing full text.
<%= rui_link("/posts/1", @post.very_long_title, truncate: true) %>
<%= rui_link("/posts/1", @post.title, truncate: 50) %>
Download Links
Create file download links with the download attribute.
<%= rui_link("/files/report.pdf", "Download Report", download: true) do |link| %>
<% link.with_icon(:download) %>
<% end %>
<%= rui_link("/files/data.csv", "Export Data", download: "monthly-export.csv") %>
URL Parameters & Anchors
Build dynamic URLs with query parameters and anchor fragments. Use empty URL with anchor for in-page navigation (table of contents, jump to section, etc).
<%= rui_link("/posts", "Filter", params: { category: "rails", sort: "recent" }) %>
<%= rui_link("", "Jump to Section", anchor: "my-section") %>
<%= rui_link("/docs/link", "Jump to Advanced", anchor: "advanced") %>
<%= rui_link("/posts", "Rails Posts", params: { category: "rails" }, anchor: "results") %>
<%= rui_link("", "Table of Contents", anchor: "toc", scroll: :smooth) %>
Full Width Links
Make links stretch to fill their container width, useful for mobile menus and card actions.
<div class="max-w-sm space-y-2">
<%= rui_link("/dashboard", "Dashboard", full_width: true) do |link| %>
<% link.with_icon(:arrow_right, position: :trailing) %>
<% end %>
<%= rui_link("/settings", "Settings", full_width: true) do |link| %>
<% link.with_icon(:settings) %>
<% end %>
</div>
<div class="max-w-sm">
<%= rui_link("/admin", "Admin Panel", full_width: true) %>
</div>
Enhanced Rel Attributes
Full control over link relationships for SEO and security.
<%= rui_link("https://example.com", "Sponsor", rel: "sponsored", external: true) %>
<%= rui_link(url, "UGC", rel: "ugc nofollow", external: true) %>
Scroll Behavior
Control scroll animation for anchor links.
<%= rui_link("#features", "Features", scroll: :smooth) %>
<%= rui_link("#top", "Back to Top", scroll: :instant) %>
Multilingual Links (HrefLang)
Indicate the language of the linked resource for SEO.
<%= rui_link("/fr/about", "À propos", hreflang: "fr") %>
<%= rui_link("/es/about", "Acerca de", hreflang: "es") %>
Referrer Policy
Control what referrer information is sent for privacy.
<%= rui_link("https://example.com", "Link", referrerpolicy: "no-referrer", external: true) %>
<%= rui_link("https://example.com", "Link", referrerpolicy: "origin", external: true) %>
Communication Links (Email, Phone, SMS)
First-class support for email, phone, and SMS with full Rails mail_to, phone_to, and sms_to compatibility.
Rails Compatibility: Supports all features from Rails helpers including subject, body, cc, bcc, reply_to, and country_code.
Email Links
Full mail_to compatibility with subject, body, CC, BCC, and reply-to:
<%= rui_link(email: "contact@example.com", text: "Email Us") %>
<%= rui_link(email: "support@example.com",
subject: "Support Request",
body: "I need help with my account",
text: "Get Support") %>
<%= rui_link(email: "team@example.com",
cc: "manager@example.com",
text: "Email Team") %>
<%= rui_link(email: "team@example.com",
cc: ["manager@example.com", "ceo@example.com"],
text: "Email Leadership") %>
<%= rui_link(email: "newsletter@example.com",
bcc: "archive@example.com",
reply_to: "support@example.com",
text: "Subscribe") %>
<%= rui_link(email: "contact@example.com",
subject: "Inquiry",
body: "I have a question",
cc: "manager@example.com",
bcc: ["archive@example.com", "log@example.com"],
reply_to: "support@example.com",
text: "Contact Sales") %>
Phone Links
Full phone_to compatibility with country code support:
<%= rui_link(phone: "555-1234", text: "Call Us") %>
<%= rui_link(phone: "555-1234",
country_code: "+1",
text: "Call US") %>
<%= rui_link(phone: "555-1234",
country_code: "1",
text: "Call US") %>
<%= rui_link(phone: "20 7946 0958",
country_code: "+44",
text: "Call UK Office") %>
SMS Links
Full sms_to compatibility with body and country code:
<%= rui_link(sms: "555-1234", text: "Text Us") %>
<%= rui_link(sms: "555-1234",
body: "Hello! I'd like more information",
text: "Send SMS") %>
<%= rui_link(sms: "555-1234",
country_code: "+1",
body: "Quick question about your product",
text: "Send Message") %>
Platform Compatibility
- ✅ Email: All options work in all major email clients
- ✅ Phone: Country codes work universally with tel: protocol
- ⚠️ SMS body: iOS (full support), Android (partial), Desktop (limited)
Real-World Examples
See how links work in common UI patterns and real-world scenarios.
Link Within Paragraph
RapidRailsUI is a ViewComponent-based UI library. Learn more about it in our documentation or check out the source code on GitHub.
<p>
Learn more in our <%= rui_link("/docs", "documentation") %> or
check the <%= rui_link("https://github.com", "code", external: true) %>
</p>
Icon Links
Perfect for actions and navigation.
<%= rui_link("#") do |link| %>
<% link.with_icon(:download) %>
Download PDF
<% end %>
Call-to-Action Links
Prominent links for important actions.
<% rui_link("/signup", size: :lg, color: :primary) do |link| %>
Get Started
<% link.with_icon(:arrow_right) %>
<% end %>
<% rui_link("/join", size: :lg, color: :blue) do |link| %>
Ready to join?
<% link.with_icon(:door_open, position: :trailing, color: :red) %>
<% end %>
<% rui_link("/pricing", size: :lg, color: :success) do |link| %>
<% link.with_icon(:credit_card) %>
View Pricing
<% end %>
Card with Action Link
Cards with prominent action links.
<div class="card">
<h4>Featured Post</h4>
<p>Description...</p>
<% rui_link("/posts/1") do |link| %>
Read more
<% link.with_icon(:arrow_right, position: :trailing) %>
<% end %>
</div>
Navigation with Active States
Using unless_current for navigation menus.
<nav>
<%= rui_link("/", "Home", unless_current: true) %>
<%= rui_link("/about", "About", unless_current: true) %>
<%= rui_link("/blog", "Blog", unless_current: true) %>
</nav>
List of Related Links
Common pattern for resource lists.
<ul>
<li>
<% rui_link("/docs/button", variant: :hover_underline) do |link| %>
<% link.with_icon(:circle) %>
Button Component
<% end %>
</li>
</ul>
Footer Links
Clean, accessible footer navigation.
<footer>
<h5>Product</h5>
<ul>
<li><%= rui_link("/features", "Features", variant: :hover_underline) %></li>
<li><%= rui_link("/pricing", "Pricing", variant: :hover_underline) %></li>
</ul>
</footer>
Accessibility
Links are built with accessibility as a priority, following WCAG guidelines and best practices.
ARIA Attributes
-
aria-disabled="true"andtabindex="-1"when disabled -
aria-busy="true"when in loading state -
aria-hidden="true"on decorative icons (loading spinner, external indicator) -
titleoraria-labelfor icon-only links -
hreflangattribute for multilingual resource hints
Keyboard Navigation
-
Tabto focus,Enterto activate -
Visible focus ring via
focus-visible:ring-2 -
Disabled links removed from tab order with
tabindex="-1"
Semantic HTML
-
Native
<a>elements with properhrefattributes -
External links include
rel="noopener noreferrer"for security - All color variants meet WCAG contrast requirements
- Truncated text auto-generates tooltip with full content
Screen Reader Support
- Use descriptive link text (avoid "click here")
- External link indicator visually communicates target opens in new tab
-
Language hints via
hreflanghelp assistive technologies
Example: Accessible Link Patterns
<%# Descriptive link text (good) %>
<%= rui_link("/pricing", "View pricing plans") %>
<%# Icon-only link with title for accessibility %>
<%= rui_link("/settings", title: "Account Settings") do |link| %>
<% link.with_icon(:settings) %>
<% end %>
<%# External link with automatic security attributes %>
<%= rui_link("https://github.com", "View on GitHub", external: true) %>
<%# Renders: rel="noopener noreferrer" target="_blank" %>
<%# Multilingual link %>
<%= rui_link("/fr/about", "À propos", hreflang: "fr") %>
<%# Disabled link (removed from tab order) %>
<%= rui_link("/admin", "Admin Panel", disabled: true) %>
<%# Renders: aria-disabled="true" tabindex="-1" %>
API Reference
rui_link
Link component with conditional rendering and full Turbo integration
| Parameter | Type | Default | Description |
|---|---|---|---|
| url* | String | — | Link URL (positional or keyword) |
| text | String | — | Link text (positional or keyword, or use block) |
Appearance
Visual styling options
| Parameter | Type | Default | Description |
|---|---|---|---|
| variant | Symbol |
:hover_underline
|
Link style variant
:underline
:no_underline
:hover_underline
:animated_underline
:nav
|
| color | Symbol |
:blue
|
Link color - semantic or Tailwind (nav defaults to :neutral) |
| size | Symbol |
:base
|
Font size
:xs
:sm
:base
:lg
:xl
|
| full_width | Boolean |
false
|
Stretch link to full container width |
Conditional Rendering
Control when link is rendered
| Parameter | Type | Default | Description |
|---|---|---|---|
| if | Boolean/Proc | — | Render link only if truthy |
| unless | Boolean/Proc | — | Render link unless truthy |
| unless_current | Boolean |
false
|
Render as text if current page |
Turbo Integration
Hotwire Turbo options
| Parameter | Type | Default | Description |
|---|---|---|---|
| turbo_frame | String | — | Target Turbo Frame ID |
| turbo_stream | Boolean |
false
|
Accept Turbo Stream response |
| turbo_method | Symbol | — |
HTTP method for Turbo
:get
:post
:put
:patch
:delete
|
| turbo_prefetch | Boolean | — | Enable/disable Turbo prefetch |
| turbo_confirm | String | — | Confirmation message |
URL Building
Build dynamic URLs with parameters and anchors
| Parameter | Type | Default | Description |
|---|---|---|---|
| params | Hash | — | Query parameters to append to URL |
| anchor | String | — | URL anchor/fragment (#section) |
| download | Boolean/String | — | Enable download; optionally specify filename |
External Links
Security options for external links
| Parameter | Type | Default | Description |
|---|---|---|---|
| external | Boolean |
auto-detected
|
Force external link behavior |
| target | String |
"_blank" for external
|
Link target |
| rel | String |
"noopener noreferrer" for external
|
Link rel attribute |
States
State options
| Parameter | Type | Default | Description |
|---|---|---|---|
| disabled | Boolean |
false
|
Disable the link |
| active | Boolean |
false
|
Active state styling |
Slots
Content slots for customizing component parts
| Slot | Description |
|---|---|
| icon | Icon slot via link.with_icon(:name, position: :leading/:trailing) |