ARTICLE AD BOX
I'm building a tabs component using ViewComponent and Stimulus, but the tab content (partials) are not rendering. I believe it's related to the active tab state.
Component Structure
tabs_component.rb:
class Dashboard::TabsComponent < ViewComponent::Base class Tab < ViewComponent::Base attr_reader :title, :active, :block def initialize(title:, active: false, &block) @title = title @active = active @block = block end def active? @active end def content(view_context) view_context.capture(&@block) if @block end end renders_many :tabs, Tab def active_tab_index tabs.index(&:active?) || 0 end endtabs_component.html.erb:
<div class="w-full" data-controller="tabs"> <!-- Tab headers --> <div> <nav class="flex space-x-8" aria-label="Tabs"> <% tabs.each_with_index do |tab, index| %> <button class="px-4 py-2 text-gray-600 hover:text-blue-600 focus:outline-none <%= 'border-b-2 border-blue-500 text-blue-600' if index == active_tab_index %>" data-tab-index="<%= index %>" data-action="click->tabs#switch" data-tabs-target="button" > <%= tab.title %> </button> <% end %> </nav> </div> <!-- Tab panels --> <div class="mt-6"> <% tabs.each_with_index do |tab, index| %> <div class="<%= 'hidden' unless index == active_tab_index %>" data-tab-index="<%= index %>" data-tabs-target="panel" > <%= tab.content(self) %> </div> <% end %> </div> </div>tabs_controller.js:
import { Controller } from "@hotwired/stimulus" export default class extends Controller { static targets = ["button", "panel"] connect() { console.log("Tabs Controller Connected") } switch(event) { event.preventDefault(); const index = event.currentTarget.dataset.tabIndex; // Remove active classes this.buttonTargets.forEach(btn => btn.classList.remove("border-b-2", "border-blue-500", "text-blue-600")) this.panelTargets.forEach(panel => panel.classList.add("hidden")) // Add active classes this.buttonTargets[index].classList.add("border-b-2", "border-blue-500", "text-blue-600") this.panelTargets[index].classList.remove("hidden") } }Usage
settings/show.html.erb:
<div class="container mx-auto pt-12 px-6"> <%= render Dashboard::TabsComponent.new do |component| %> <% component.with_tab(title: "General") do %> General <% end %> <% component.with_tab(title: "Appearance") do %> <%= render "settings/appearance" %> <% end %> <% component.with_tab(title: "Appointments") do %> <%= render "settings/appointments" %> <% end %> <% end %> </div>Problem
The tab buttons render correctly, but none of the tab content (including the partials) is showing up. The panels appear to all be hidden.
What I've Tried
Verified the partials exist and render correctly when used outside the component Checked browser console - Stimulus controller connects successfully Inspected the DOM - panels have the hidden class appliedWhat am I missing? Why isn't the first tab showing by default?
