Components
Lato provides a set of reusable functional components.
By using Lato components, you can save development time and simplify maintenance tasks.
All components are available via the Lato::ComponentsHelper helper.
Source code: Lato::ComponentsHelper
Navbar
Navbar nav item
Component to render a menu item in the navbar.
Parameters:
- key: the menu item's key. Used to activate the item via the active_navbar function from the Lato::Layoutable concern.
- path: a path or absolute URL to redirect the user.
- &block: block of HTML to render inside the menu item.
<%= lato_navbar_nav_item :account, lato.account_path do %> Account <% end %>
Navbar nav locales item
Component to render a locale-switcher dropdown in the navbar.
Parameters:
- options (optional): hash of options.
- flag (default: true): if true, displays flag icons next to locale names.
<%= lato_navbar_nav_locales_item %> <%= lato_navbar_nav_locales_item flag: false %>
Notes
The locale switcher automatically detects available locales from I18n.available_locales and renders a dropdown to switch between them.
When the user changes locale, the switch_locale action on Lato::ApplicationController is triggered, which sets a cookie and redirects back.
Sidebar
Sidebar nav item
Component to render a menu item in the sidebar.
Parameters:
- key: the menu item's key. Used to activate the item via the active_sidebar function from the Lato::Layoutable concern.
- path: a path or absolute URL to redirect the user.
- &block: block of HTML to render inside the menu item.
There are also some extra parameters to customize the menu item:
- external (default: false): if true, an icon is added to indicate that the link opens in a new tab and a target="_blank" attribute is added to the link.
- children (default: nil): an array of hashes representing submenu items. Each hash should contain the keys :key, :label, and :path. If this parameter is provided, the menu item will have a collapsible submenu.
<%= lato_sidebar_nav_item :account, lato.account_path do %>
Account
<% end %>
<%= lato_sidebar_nav_item :github, 'https://github.com/Lato-org/lato', external: true do %>
GitHub
<% end %>
<%= lato_sidebar_nav_item :examples, '#', children: [
{ key: :products, label: 'CRUD Example', path: main_app.products_path },
{ key: :other, label: 'Other Examples soon..', path: '#' },
] do %>
Examples
<% end %>
Page
Page head
<%= lato_page_head 'Page title' %>
Page title
<%= lato_page_head 'Page title', [{ label: 'Title 1', path: main_app.page_path }, { label: 'Title 2' }] %>
Page title
<%= lato_page_head 'Page title' do %> <p class="lead">Welcome to this page</p> <% end %>
Page title
Welcome to this page
Form
Forms can be generated using the lato_form components.
lato_form components are a set of utilities designed to work with Rails' form_with helper and the Stimulus controller lato_form_controller.
Below is a list of available components with usage examples:
-
lato_form_noticesdisplays success feedback (notices set by the controller action). -
lato_form_errorsdisplays error feedback (based on model instance errors). -
lato_form_item_labelrenders the field label. -
lato_form_item_input_textrenders a text input. -
lato_form_item_input_numberrenders a number input. -
lato_form_item_input_emailrenders an email input. -
lato_form_item_input_passwordrenders a password input. -
lato_form_item_input_checkrenders a checkbox input. -
lato_form_item_input_selectrenders a select input. -
lato_form_item_input_filerenders a file input. -
lato_form_item_input_file_dropzonerenders a file input with drag-and-drop support. -
lato_form_item_input_textarearenders a textarea input. -
lato_form_item_input_daterenders a date input. -
lato_form_item_input_datetimerenders a datetime input. -
lato_form_item_input_timerenders a time input. -
lato_form_item_input_colorrenders a color input. -
lato_form_item_input_signaturerenders a signature input (draw on canvas or upload image). -
lato_form_submitrenders the submit button.
<%
user ||= @session.user
%>
<%= turbo_frame_tag 'tutorial_form-example' do %>
<%= form_with model: user, url: main_app.components_update_user_action_path, data: {
turbo_frame: '_self',
controller: 'lato-form'
} do |form| %>
<%= lato_form_notices class: %w[mb-3] %>
<%= lato_form_errors user, class: %w[mb-3] %>
<div class="row">
<div class="col col-12 col-lg-6 mb-3">
<%= lato_form_item_label form, :first_name, 'Name' %>
<%= lato_form_item_input_text form, :first_name, required: true %>
</div>
<div class="col col-12 col-lg-6 mb-3">
<%= lato_form_item_label form, :last_name, 'Surname' %>
<%= lato_form_item_input_text form, :last_name, required: true %>
</div>
</div>
<div class="row">
<div class="col col-12 col-lg-6 mb-3">
<%= lato_form_item_label form, :select, 'Select' %>
<%= lato_form_item_input_select form, :select, [['Type 1', '1'], ['Type 2', '2']], required: true %>
</div>
<div class="col col-12 col-lg-6 mb-3">
<%= lato_form_item_label form, :file, 'File' %>
<%= lato_form_item_input_file form, :file, required: true %>
</div>
</div>
<div class="row">
<div class="col col-12 mb-3">
<%= lato_form_item_label form, :textarea, 'Textarea' %>
<%= lato_form_item_input_textarea form, :textarea, required: true %>
</div>
</div>
<div class="row">
<div class="col col-12 col-lg-4 mb-3">
<%= lato_form_item_label form, :date, 'Date' %>
<%= lato_form_item_input_date form, :date, required: true %>
</div>
<div class="col col-12 col-lg-4 mb-3">
<%= lato_form_item_label form, :datetime, 'Datetime' %>
<%= lato_form_item_input_datetime form, :datetime, required: true %>
</div>
<div class="col col-12 col-lg-4 mb-3">
<%= lato_form_item_label form, :time, 'Time' %>
<%= lato_form_item_input_time form, :time, required: true %>
</div>
</div>
<div class="row">
<div class="col col-12 mb-3">
<%= lato_form_item_label form, :color, 'Color' %>
<%= lato_form_item_input_color form, :color, required: true %>
</div>
</div>
<div class="row">
<div class="col col-12 mb-3">
<%= lato_form_item_label form, :first_name, 'Name with auto-complete' %>
<%= lato_form_item_input_text form, :first_name, required: true, data: {
controller: 'lato-input-autocomplete',
lato_input_autocomplete_path_value: products_autocomplete_path
} %>
</div>
</div>
<div class="row">
<div class="col col-12 mb-3">
<%= lato_form_item_label form, :first_name, 'Name with auto-complete V2' %>
<%= lato_form_item_input_text form, :first_name, required: true, data: {
controller: 'lato-input-autocomplete2',
lato_input_autocomplete2_path_value: products_autocomplete_path
} %>
</div>
</div>
<div class="row">
<div class="col col-12 mb-3">
<%= lato_form_item_label form, :file_dropzone, 'File Dropzone' %>
<%= lato_form_item_input_file_dropzone form, :file_dropzone, required: true %>
</div>
</div>
<div class="d-flex justify-content-end">
<%= lato_form_submit form, 'Update', class: %w[btn-success] %>
</div>
<% end %>
<% end %>
Signature input
The lato_form_item_input_signature component allows the user to provide a signature either by drawing it on a canvas or by uploading a PNG/JPEG image.
The result is stored as a standard Active Storage attachment on the model.
Parameters:
- form: the form builder object.
- key: the attribute name of the has_one_attached attachment (e.g. :signature).
The model must declare the attachment:
class Product < ApplicationRecord has_one_attached :signature end
Usage in a form:
<div class="mb-3"> <%= lato_form_item_label form, :signature %> <%= lato_form_item_input_signature form, :signature %> </div>
When an existing signature is already attached, a third tab Current signature is shown as the default active tab, displaying the current image.
The user can switch to the Draw or Upload image tabs to replace it.
If the user submits the form while staying on the "Current signature" tab, the existing attachment is preserved unchanged.
The component is powered by the Stimulus controller lato-input-signature and requires no additional configuration.
Nested attributes
This component allows you to easily manage collections of associated records within a parent form.
The lato_form_item_input_list component requires three parameters:
- form: the form builder object.
- key: the attribute name representing the collection of associated records.
- partial_path: the path to the partial that will be used to render each item in the collection.
- partial_params (optional): a hash of additional parameters to pass to the partial.
The lato_form_item_input_list component will render the list of items associated to the form object record and a button to add new items.
Each item will be rendered using the specified partial. The partial will receive the following local variables:
- form: the form builder object for the item.
- *partial_params: any additional parameters passed to the component.
The partial used to render each item should contain only the fields related to the item.
Other hidden fields (such as the item ID) and the remove button are automatically handled by the component.
<%= lato_form_item_input_list(form, :product_items, 'products/input_list_product_item') %>
Example: main form
Example: form item partial
Example: controller params
Example: model configuration
Data
Data badge
The lato_data_badge component allows easy rendering of badges with different styles.
<%= lato_data_badge "badge primary", "primary" %> <%= lato_data_badge "badge secondary", "secondary" %> <%= lato_data_badge "badge info", "info" %> <%= lato_data_badge "badge success", "success" %> <%= lato_data_badge "badge warning", "warning" %> <%= lato_data_badge "badge danger", "danger" %> <%= lato_data_badge "badge light", "light" %> <%= lato_data_badge "badge dark", "dark" %>
Data user
The lato_data_user component allows easy rendering of user information with avatar and full name.
<%= lato_data_user("Mr. Wolf") %>
<%= lato_data_user("Ms. White", "https://i.pravatar.cc/40?img=5") %>
Index
The lato_index component allows easy visualization of database records.
Index (front-end)
The simplest way to use the component is to pass it an ActiveRecord collection:
<%= lato_index Product.all.limit(5) %>
Customizing columns
Columns can be customized using the :columns parameter:
<%= lato_index Product.all.limit(5), columns: %i[id code status] %>
Notes
Column titles can be customized using Rails translation files.
Example:
en:
activerecord:
attributes:
product:
code: Code
status: Status
Customizing column content
You can customize column content by creating helper methods with the format model_column(record).
For example, to customize the output of the :lato_user_id column, define the following helper in ProductsHelper:
class ProductsHelper
def product_lato_user_id(product)
product.lato_user.full_name
end
end
Adding custom columns
To add custom columns, declare a method in the model that provides the value.
For example, after adding a lifetime method to the Product model, you can use the :lifetime column:
class Product < ApplicationRecord
def lifetime
Time.now - created_at
end
end
class ProductsHelper
def product_lifetime(product)
Time.at(product.lifetime).utc.strftime('%H h %M m')
end
end
<%= lato_index Product.all.limit(5), columns: %i[id code status lifetime] %>
Notes
Custom columns can also be translated using Rails translation files, just like database-backed columns.
To add columns that don't map to real attributes, use attr_accessor :column_name instead of defining a method in the model:
class Product < ApplicationRecord attr_accessor :lifetime end
Adding actions
<%= lato_index Product.all.limit(5), custom_actions: {
create: {
path: products_create_path,
icon: 'bi bi-plus',
aria_label: 'Create product',
title: 'Create product'
}
} %>
Adding in-page actions
<%= lato_index Product.all.limit(5), custom_actions: {
create: {
path: products_create_path,
icon: 'bi bi-plus',
data: { lato_action_target: 'trigger', turbo_frame: dom_id(Product.new, 'form') },
aria_label: 'Create product',
title: 'Create product'
}
} %>
Notes
The :create_turbo_frame option specifies the turbo-frame ID in the products_create_path page that should be inserted into the overlay.
Adding dropdown actions
You can also add a dropdown menu of actions using the :dropdown_actions parameter.
This renders a button with a dropdown menu containing a list of actions.
<%= lato_index Product.all.limit(5), dropdown_actions: {
icon: 'bi bi-three-dots-vertical',
actions: [
{ path: export_products_path, icon: 'bi bi-download', title: 'Export CSV' },
{ path: import_products_path, icon: 'bi bi-upload', title: 'Import CSV' },
]
} %>
Adding row-specific actions
Row-specific actions can be added through custom columns (as shown above).
These actions can also be opened in-page via overlays using data-lato-index-target="action" and data-turbo-frame="turbo-frame-ID".
class ProductsHelper
def product_actions(product)
content_tag(:div, class: 'btn-group btn-group-sm') do
concat link_to(
'Edit',
products_update_path(product),
class: 'btn btn-primary',
data: { lato_action_target: 'trigger', turbo_frame: dom_id(product, 'form') }
)
end
end
end
<%= lato_index Product.all.limit(5), columns: %i[id code status lifetime actions] %>
Notes
Instead of using per-model helper methods (like product_lato_user_id), you can also override column rendering globally by defining lato_index_dynamic_label and lato_index_dynamic_value methods in your ApplicationHelper:
These methods receive a hash with :column, :model_name, :record, and :value keys, allowing you to centralize custom column rendering logic.
module ApplicationHelper
def lato_index_dynamic_label(params = {})
# Return a custom label for specific columns
return 'Custom Label' if params[:column] == :my_column
super
end
def lato_index_dynamic_value(params = {})
# Return a custom value for specific columns
return "Custom: #{params[:value]}" if params[:column] == :my_column
super
end
end
Index (back-end)
To make the index fully interactive, use the lato_index_collection function from the Lato::Componentable concern in the controller.
def index @products = lato_index_collection(Product.all, pagination: true) end
<%= lato_index @products %>
Customizing columns
def index
@products_columns = lato_index_collection(
Product.all,
pagination: true,
columns: %i[code status id]
)
end
<%= lato_index @products_columns %>
Customizing pagination size
Users can choose how many items to display per page using a selector enabled via the :pagination_options parameter in lato_index_collection.
def index
@products_pagination_options = lato_index_collection(
Product.all,
pagination: 10,
columns: %i[code status id],
key: 'products_pagination_options'
)
end
<%= lato_index @products_pagination_options, pagination_options: [10,20,50,100] %>
Making columns sortable
def index
@products_sortable_columns = lato_index_collection(
Product.all,
pagination: true,
columns: %i[code status lato_user_id],
sortable_columns: %i[code status lato_user_id]
)
end
<%= lato_index @products_sortable_columns %>
Notes
To customize sorting logic, define the lato_index_order scope inside the model.
Example:
class Product < ApplicationRecord
belongs_to :lato_user, class_name: 'Lato::User'
scope :lato_index_order, ->(column, order) do
return joins(:lato_user).order("lato_users.last_name #{order}, lato_users.first_name #{order}") if column == :lato_user_id
order("#{column} #{order}")
end
end
Notes
To set a default sorting column, use the default_sort_by option in the lato_index_collection method.
Example:
def index
@products_default_sort = lato_index_collection(
Product.all,
pagination: true,
columns: %i[code status lato_user_id],
sortable_columns: %i[code status lato_user_id],
default_sort_by: 'code|asc' # 'column|direction'
)
end
Making columns searchable
def index
@products_searchable_columns = lato_index_collection(
Product.all,
pagination: true,
columns: %i[code status lato_user_id],
sortable_columns: %i[code status lato_user_id],
searchable_columns: %i[code lato_user_id]
)
end
<%= lato_index @products_searchable_columns %>
Notes
To customize search logic, define the lato_index_search scope inside the model.
Example:
class Product < ApplicationRecord
belongs_to :lato_user, class_name: 'Lato::User'
scope :lato_index_search, ->(search) do
joins(:lato_user).where("
lower(code) LIKE :search OR
lower(lato_users.first_name) LIKE :search OR
lower(lato_users.last_name) LIKE :search
", search: "%#{search.downcase.strip}%")
end
end
Skip total count for large tables
For large tables, you can skip the total count to improve performance by setting the :skip_total_count option to true in the lato_index method.
This is useful when you don't need the total count of records, such as when displaying a large collection.
<%= lato_index @products_skip_total_count_columns, skip_total_count: true %>