Common Patterns
Rendering lists, conditionals, and other patterns you'd reach for in Vue/React — translated to ERB.
Conditional rendering
Vue uses v-if. React uses ternaries or early returns. In ERB, it’s just
Ruby:
Vue / React
<Badge v-if="user.admin" color="warning">
Admin
</Badge>
{user.admin && <Badge color="warning">Admin</Badge>}
Kiso
<%% if user.admin? %>
<%%= kui(:badge, color: :warning) { "Admin" } %>
<%% end %>
Rendering lists
Vue uses v-for. React uses .map(). In ERB, use .each:
<% @users.each do |user| %>
<%= kui(:badge, color: user.role_color) { user.name } %>
<% end %>
For components that support it, Kiso also has a collection: shorthand:
<%= kui(:badge, collection: @tags) %>
Dynamic props
Compute locals from your data just like you would with dynamic props:
<%= kui(:alert,
color: flash_type == "notice" ? :success : :error,
variant: :soft) do %>
<%= kui(:alert, :title) { flash_type.capitalize } %>
<%= kui(:alert, :description) { flash_message } %>
<% end %>
Composing components together
Build complex UI by nesting components. This Card with a list of Badges is the same pattern as nesting React or Vue components:
<%= kui(:card) do %>
<%= kui(:card, :header) do %>
<%= kui(:card, :title) { "Team Members" } %>
<%= kui(:card, :description) { "People with access to this project." } %>
<% end %>
<%= kui(:card, :content) do %>
<%= kui(:table) do %>
<%= kui(:table, :header) do %>
<%= kui(:table, :row) do %>
<%= kui(:table, :head) { "Name" } %>
<%= kui(:table, :head) { "Role" } %>
<% end %>
<% end %>
<%= kui(:table, :body) do %>
<% @members.each do |member| %>
<%= kui(:table, :row) do %>
<%= kui(:table, :cell) { member.name } %>
<%= kui(:table, :cell) do %>
<%= kui(:badge, color: member.role_color, size: :sm) { member.role } %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
Stimulus for interactivity
Where Vue/React use state and event handlers, Kiso uses Stimulus controllers:
<%= kui(:button,
data: { controller: "clipboard", action: "click->clipboard#copy",
clipboard_content_value: invite_url }) do %>
Copy invite link
<% end %>
The component handles the HTML and styling. The Stimulus controller handles behavior. They’re separate concerns — you can swap the component’s appearance without touching the JS, or reuse the controller on a completely different element.